Thread overview | |||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
April 20, 2021 write once type? | ||||
---|---|---|---|---|
| ||||
I have had the need in some cases to *maybe* set a const value inside a loop. One can sometimes abstract this into a lambda function, but sometimes this is not possible (e.g. if the loop is static). Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop. My use case is I have a property that is const inside a set of types, and I want to verify that they all have the same value, and extract what that value is. I'm wondering if anyone has a "Write once" type, that is, a type that allows you to write it exactly once, and is treated like initialization on first setting (i.e. allows writing to previously unused const data). This type has to decide at runtime whether it has been set. So it would kind of be like Nullable!T, but Nullable doesn't allow setting when the T is const. Optional!T doesn't work either. -Steve |
April 20, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 4/20/21 3:56 PM, Steven Schveighoffer wrote:
> I have had the need in some cases to *maybe* set a const value inside a loop. One can sometimes abstract this into a lambda function, but sometimes this is not possible (e.g. if the loop is static). Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop.
>
> My use case is I have a property that is const inside a set of types, and I want to verify that they all have the same value, and extract what that value is.
>
> I'm wondering if anyone has a "Write once" type, that is, a type that allows you to write it exactly once, and is treated like initialization on first setting (i.e. allows writing to previously unused const data).
>
> This type has to decide at runtime whether it has been set. So it would kind of be like Nullable!T, but Nullable doesn't allow setting when the T is const. Optional!T doesn't work either.
I just realized, this is Rebindable, or tail-const. (I don't need the head to be truly const, I just need to be able to copy into a const-referring thing). I currently am using a pointer, which is working, but I prefer not to use it, and it is not general enough.
-Steve
|
April 20, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tue, Apr 20, 2021 at 03:56:33PM -0400, Steven Schveighoffer via Digitalmars-d-learn wrote: [...] > I'm wondering if anyone has a "Write once" type, that is, a type that allows you to write it exactly once, and is treated like initialization on first setting (i.e. allows writing to previously unused const data). Maybe a const class? The reference is null until you initialize it by allocating a new object and initializing it in the ctor. But sounds like you want a value type instead. Technically, allocating a const class involves the GC assigning some region of memory to the class, initializing it, then casting it to const. So I'd imagine that the by-value equivalent would require a const cast somewhere, probably in a @trusted block if you want it to work with @safe. Which means that probably you'll need a @trusted cast somewhere in your implementation. So perhaps something like this: struct WriteOnce(T) { const T payload; const bool isSet; void opAssign(U : T)(U data) in (!isSet) { assert(!isSet); @trusted() { *(cast()&payload) = data; *(cast()&isSet) = true; }(); } } T -- Prosperity breeds contempt, and poverty breeds consent. -- Suck.com |
April 20, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer wrote: > I have had the need in some cases to *maybe* set a const value inside a loop. One can sometimes abstract this into a lambda function, but sometimes this is not possible (e.g. if the loop is static). Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop. > > My use case is I have a property that is const inside a set of types, and I want to verify that they all have the same value, and extract what that value is. > > I'm wondering if anyone has a "Write once" type, that is, a type that allows you to write it exactly once, and is treated like initialization on first setting (i.e. allows writing to previously unused const data). > > This type has to decide at runtime whether it has been set. So it would kind of be like Nullable!T, but Nullable doesn't allow setting when the T is const. Optional!T doesn't work either. > > -Steve Not quite your use case, I think, but std.typecons.Rebindable at least allows you to bind a new const(object). Not write once though. I think I found that after a hint of its existence in https://forum.dlang.org/thread/orpbvvjspibfpitnnpxd@forum.dlang.org |
April 20, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 20 April 2021 at 20:04:17 UTC, Steven Schveighoffer wrote:
> I just realized, this is Rebindable, or tail-const. (I don't need the head to be truly const, I just need to be able to copy into a const-referring thing). I currently am using a pointer, which is working, but I prefer not to use it, and it is not general enough.
>
> -Steve
Ah, you found it - I was looking up the reference for it and you replied in the mean-time. Ignore my answer :)
|
April 25, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer wrote:
> I have had the need in some cases to *maybe* set a const value inside a loop. One can sometimes abstract this into a lambda function, but sometimes this is not possible (e.g. if the loop is static). Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop.
>
> [...]
This is a feature that I wanted always since my C# and C++ time. I've tried to mimic it with class/struct (including readonly with C++) but it isn't same thing.
|
April 29, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer wrote:
> Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop.
Can I ask why you require to handle it in one loop?
As I see it, you are required to poll over isSet/isNotSet in every iteration.
|
April 29, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to sighoya | On 4/29/21 5:31 PM, sighoya wrote:
> On Tuesday, 20 April 2021 at 19:56:33 UTC, Steven Schveighoffer wrote:
>> Not only that, but I may also want to keep processing the loop and do something different if the value has already been set instead of returning immediately, which necessitates a second loop.
>
> Can I ask why you require to handle it in one loop?
> As I see it, you are required to poll over isSet/isNotSet in every iteration.
>
In my case, for value of a certain type in the loop, I was storing a specific field from the first one I found, and then verifying that all the other values of that type (not exactly the same type, but similar) had the same value for that field, otherwise it was an error.
I could have done it in 2 loops, but it's wasteful.
-Steve
|
April 30, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Friday, 30 April 2021 at 01:30:54 UTC, Steven Schveighoffer wrote:
> In my case, for value of a certain type in the loop, I was storing a specific field from the first one I found, and then verifying that all the other values of that type (not exactly the same type, but similar) had the same value for that field, otherwise it was an error.
>
> I could have done it in 2 loops, but it's wasteful.
I have problems to grok exactly what you're talking about, can you provide some neat and small example for this?
|
April 30, 2021 Re: write once type? | ||||
---|---|---|---|---|
| ||||
Posted in reply to sighoya | On 4/30/21 9:24 AM, sighoya wrote:
> On Friday, 30 April 2021 at 01:30:54 UTC, Steven Schveighoffer wrote:
>
>> In my case, for value of a certain type in the loop, I was storing a specific field from the first one I found, and then verifying that all the other values of that type (not exactly the same type, but similar) had the same value for that field, otherwise it was an error.
>>
>> I could have done it in 2 loops, but it's wasteful.
>
> I have problems to grok exactly what you're talking about, can you provide some neat and small example for this?
>
>
I'll just show you the code here. This is from my (up and coming) sql builder project.
A `ColumnDef!T` is an sql table column of type `T`, which contains an expression describing the column, and a table definition of where the column comes from. `TableDef` is const inside the column def:
```d
struct ColumnDef(T)
{
const TableDef table;
ExprString expr;
alias type = T;
}
```
Now, what if you wanted a "computed" column? that is, you wanted to define a column with a type, but that is computed from other columns? Like `select total - tax as netcost from sometable`
What I want to create is something that returns a ColumnDef from an arbitrary expression. But there is only one table definition, so if you want to return a ColumnDef, you need to ensure there is only one source table. So here is my code that does that:
```d
ColumnDef!T exprCol(T, Args...)(Args args)
{
// first, find all columns, and ensure that table defs are all from the
// same table (a ColumnDef cannot have multiple tables).
const(TableDef)* tabledef;
foreach(ref arg; args)
{
static if(is(typeof(arg) == ColumnDef!U, U))
{
if(tabledef && arg.table != *tabledef)
throw new Exception("can't have multiple tabledefs in the expression");
else
tabledef = &arg.table;
}
}
assert(tabledef !is null);
// build the expr string
ExprString expr;
foreach(ref a; args)
{
static if(is(typeof(a) == string))
expr ~= a;
else
expr ~= a.expr;
}
return ColumnDef!(T)(*tabledef, expr);
}
```
The pointer abstraction works perfectly, but if there was no way to use that abstraction, there aren't any D facilities to allow this. It's really tail-const that I need.
I could have looped 2x over the args, and used an inner function to fetch the first one (or maybe used a staticIndexOf to get the first ColumnDef thing), and a second loop to verify all the remaining args have the same table def, but this loop works just as designed and is super-readable.
-Steve
|
Copyright © 1999-2021 by the D Language Foundation