Re: Opinions on iterating a struct to absorb the decoding of a CSV?

2024-03-31 Thread cc via Digitalmars-d-learn

On Monday, 1 April 2024 at 04:54:46 UTC, cc wrote:
I scoured [Traits](https://dlang.org/spec/traits.html) and 
[std.traits](https://dlang.org/phobos/std_traits.html) looking 
for a simple method to tell whether a member was declared as 
enum but couldn't find one, so if anyone knows a proper way to 
do it please let me know.


Turns out this can be done as part of the panacea that is `is()` 
statements.

```d
static if (is(typeof({enum X = SYM;})))
```


Re: Opinions on iterating a struct to absorb the decoding of a CSV?

2024-03-31 Thread cc via Digitalmars-d-learn

On Thursday, 28 March 2024 at 17:23:39 UTC, Andy Valencia wrote:
I wanted a lightweight and simpler CSV decoder.  I won't post 
the whole thing, but basically you instantiate one as:


That's pretty much the best way to do it.  While `.tupleof` does 
look kind of hacky, and you could instead iterate using 
`___traits(allMembers, T)` and `__traits(getMember, T, 
"symbolname")` which looks more self-documenting, this ends up 
being more of a pain, because it's going to iterate through 
everything in the type, including functions, 
constructors/destructors, aliases, enums, static members, 
inherited members in classes, etc etc, and it's a plate of 
spaghetti to sort them all out, plus there are issues with 
aliasing on top of that.  Sometimes you might want to do that of 
course, but for simple situations like this, `.tupleof` just 
works.


```d
struct Foo {
int x = 1;
float f = 3.14;
string abc = "abc";
string[] xyz;
immutable int i;
const int c;

this(int x) { this.x = x; i = 7; c = 8; }
this(float f) { this.f = f; }
bool speak() { return false; }
bool speak(bool b) { return b; }
	void toString(scope void delegate(const(char)[]) writer) { 
/*...*/ }

alias X = int;
enum E = 3;
static enum F = 4;
static int y = 5;
}
void main() {
Foo foo;
dumpInfo(foo);
}
void dumpInfo(T)(T t) {
import std.traits;
static foreach (idx, field; T.tupleof) {{
alias TYPE = typeof(field);
enum NAME = field.stringof;
writefln("[%s] %s => %s", TYPE.stringof, NAME, t.tupleof[idx]);
}}
writeln;

static foreach (sym; __traits(allMembers, T)) {{
enum bool ISSTATIC = hasStaticMember!(T, sym);
static if (ISSTATIC)
alias SYM = __traits(getMember, T, sym);
else
alias SYM = __traits(getMember, t, sym);
enum NAME = sym;
static if (isType!SYM) { // aliases
writefln("(TYPE) %s : %s", NAME, SYM.stringof);
} else static if (isFunction!SYM) {
alias OVERLOADS = __traits(getOverloads, T, sym);
static foreach (idx, FUNC; OVERLOADS) {
writefln("(FUNC) %s<%s> : %s", NAME, idx, 
typeof(FUNC).stringof);
writefln("\t%s %s %s", ReturnType!FUNC.stringof, 
Parameters!FUNC.stringof, 
ParameterIdentifierTuple!FUNC.stringof); // Useful

}
} else {
alias TYPE = typeof(SYM);

// where is isEnum or isManifestConstant?
			enum bool ISASSIGNABLE = __traits(compiles, 
{__traits(getMember, t, sym) = __traits(getMember, t, sym);});
			enum bool ISASSIGNABLE_IN_CTOR = __traits(compiles, 
{cast()__traits(getMember, t, sym) = cast()__traits(getMember, t, 
sym);});


static if (!ISASSIGNABLE && !ISASSIGNABLE_IN_CTOR) {
// MAYBE it's an enum.  Or something else 
unassignable.
writefln("(ENUM) [%s] %s => %s", TYPE.stringof, 
NAME, SYM);
} else static if (ISSTATIC) {
writefln("(STATIC) [%s] %s => %s", 
TYPE.stringof, NAME, SYM);
} else {
writefln("[%s] %s => %s", TYPE.stringof, NAME, 
__traits(getMember, t, sym)); // SYM doesn't work here

}
}
}}
}
```

```
[int] x => 1
[float] f => 3.14
[string] abc => abc
[string[]] xyz => []
[immutable(int)] i => 0
[const(int)] c => 0

[int] x => 1
[float] f => 3.14
[string] abc => abc
[string[]] xyz => []
[immutable(int)] i => 0
[const(int)] c => 0
(FUNC) __ctor<0> : ref Foo(int x)
Foo (int) AliasSeq!("x")
(FUNC) __ctor<1> : ref Foo(float f)
Foo (float) AliasSeq!("f")
(FUNC) speak<0> : bool()
bool () ()
(FUNC) speak<1> : bool(bool b)
bool (bool) AliasSeq!("b")
(FUNC) toString<0> : void(scope void delegate(const(char)[]) 
writer)

void (scope void delegate(const(char)[])) AliasSeq!("writer")
(TYPE) X : int
(ENUM) [int] E => 3
(ENUM) [int] F => 4
(STATIC) [int] y => 5
```

I scoured [Traits](https://dlang.org/spec/traits.html) and 
[std.traits](https://dlang.org/phobos/std_traits.html) looking 
for a simple method to tell whether a member was declared as enum 
but couldn't find one, so if anyone knows a proper way to do it 
please let me know.


Re: Opinions on iterating a struct to absorb the decoding of a CSV?

2024-03-28 Thread H. S. Teoh via Digitalmars-d-learn
On Thu, Mar 28, 2024 at 05:23:39PM +, Andy Valencia via Digitalmars-d-learn 
wrote:
[...]
> auto t = T();
> foreach (i, ref val; t.tupleof) {
> static if (is(typeof(val) == int)) {
> val = this.get_int();
> } else {
> val = this.get_str();
> }
> }
> return t;
> 
> So you cue off the type of the struct field, and decode the next CSV
> field, and put the value into the new struct.
> 
> Is there a cleaner way to do this?  This _does_ work, and gives me
> very compact code.

This is pretty clean, and is a good example of DbI. I use the same
method in my fastcsv experimental module to transcribe csv to an array
of structs:

https://github.com/quickfur/fastcsv


T

-- 
Today's society is one of specialization: as you grow, you learn more and more 
about less and less. Eventually, you know everything about nothing.


Opinions on iterating a struct to absorb the decoding of a CSV?

2024-03-28 Thread Andy Valencia via Digitalmars-d-learn
I wanted a lightweight and simpler CSV decoder.  I won't post the 
whole thing, but basically you instantiate one as:


struct Whatever {
   ...
}
...
f = File("path.csv", "r");
auto c = CSVreader!Whatever(f);
foreach (rec; c) { ...

CSVreader is, of course, templated:

struct CSVreader(T) {
...
}

and the innermost bit of CSVreader is:

auto t = T();
foreach (i, ref val; t.tupleof) {
static if (is(typeof(val) == int)) {
val = this.get_int();
} else {
val = this.get_str();
}
}
return t;

So you cue off the type of the struct field, and decode the next 
CSV field, and put the value into the new struct.


Is there a cleaner way to do this?  This _does_ work, and gives 
me very compact code.