Thread overview
'strong types' a la boost
Mar 14, 2015
Charles Cooper
Mar 14, 2015
Charles Cooper
Mar 14, 2015
Namespace
Mar 14, 2015
Charles Cooper
Mar 14, 2015
Namespace
Mar 14, 2015
Brad Anderson
March 14, 2015
I was wondering what the idiomatic D way of implementing strong types. Boost has something along these lines using classes:
http://www.boost.org/doc/libs/1_37_0/boost/strong_typedef.hpp

When programming in C++ I find that the compiler does not necessarily generate good code with these types, and I usually use C++11 enum class, e.g.

enum class dollars_t : uint32_t {}
enum class cents_t : uint32_t {}
/* .. code e.g. for converting between cents and dollars .. */
void do_something_with_dollars(dollars_t) {} // this will fail to compile if you try to pass it cents_t or uint32_t

This is obviously a gross abuse of the enum class feature.

I think there is also a way of doing this (in C++) using templates a la std::chrono

But enough about C++. Is there an idiomatic way of doing this in D, if so what is it?

Thanks!
Charles
March 14, 2015
I think I may have answered my own question. It seems std.typecon provides a facility for this.
http://dlang.org/phobos/std_typecons.html#.Proxy
http://dlang.org/phobos/std_typecons.html#.Typedef

Is this the 'right' way to do things? It seems that Proxy is used as a mixin whereas Typedef is used to create standalone types.

Thanks
Charles

On Saturday, 14 March 2015 at 14:55:06 UTC, Charles Cooper wrote:
> I was wondering what the idiomatic D way of implementing strong types. Boost has something along these lines using classes:
> http://www.boost.org/doc/libs/1_37_0/boost/strong_typedef.hpp
>
> When programming in C++ I find that the compiler does not necessarily generate good code with these types, and I usually use C++11 enum class, e.g.
>
> enum class dollars_t : uint32_t {}
> enum class cents_t : uint32_t {}
> /* .. code e.g. for converting between cents and dollars .. */
> void do_something_with_dollars(dollars_t) {} // this will fail to compile if you try to pass it cents_t or uint32_t
>
> This is obviously a gross abuse of the enum class feature.
>
> I think there is also a way of doing this (in C++) using templates a la std::chrono
>
> But enough about C++. Is there an idiomatic way of doing this in D, if so what is it?
>
> Thanks!
> Charles

March 14, 2015
You can do it this way:
----
struct dollars_t {
    uint _dollar;

    this(uint d) {
        _dollar = d;
    }

    alias _dollar this;
}

struct cents_t {
    uint _cent;

    this(uint c) {
        _cent = c;
    }

    alias _cent this;
}

void do_something_with_dollars(dollars_t d) {
    writeln(d);
}

void main() {
    dollars_t d = 1;

    do_something_with_dollars(d);

    cents_t c = 2;

    //do_something_with_dollars(c);
    //do_something_with_dollars(2);
}
----

Or you can create your own small TypeDef:

----
struct TypeDef(T, size_t l = __LINE__) {
    T _val;

    this(T v) {
        _val = v;
    }

    alias _val this;
}

alias dollars_t = TypeDef!(uint);
alias cents_t = TypeDef!(uint);
----

Thanks to the second template parameter 'l' the template instances of dollars_t and cents_t aren't equal.
March 14, 2015
Interesting. I think in the second example there are pathological cases where one has similar declarations in two modules at the same line.

moduleA.d:100
alias dollars_t TypeDef!int;
moduleB.d:100
alias cents_t TypeDef!int;

main.d:
import moduleA;
import moduleB;
void write_dollars_to_database(dollars_t x) {
  /* code */
}
void main() {
  cents_t cents;
  write_dollars_to_database(cents); // compilation succeeds, bank fails
}

However, I see your point, I think it can be gotten around by using a combination of the __LINE__, __FILE__ and __MODULE__ directives!

Charles

On Saturday, 14 March 2015 at 16:01:15 UTC, Namespace wrote:
> You can do it this way:
> ----
> struct dollars_t {
>     uint _dollar;
>
>     this(uint d) {
>         _dollar = d;
>     }
>
>     alias _dollar this;
> }
>
> struct cents_t {
>     uint _cent;
>
>     this(uint c) {
>         _cent = c;
>     }
>
>     alias _cent this;
> }
>
> void do_something_with_dollars(dollars_t d) {
>     writeln(d);
> }
>
> void main() {
>     dollars_t d = 1;
>
>     do_something_with_dollars(d);
>
>     cents_t c = 2;
>
>     //do_something_with_dollars(c);
>     //do_something_with_dollars(2);
> }
> ----
>
> Or you can create your own small TypeDef:
>
> ----
> struct TypeDef(T, size_t l = __LINE__) {
>     T _val;
>
>     this(T v) {
>         _val = v;
>     }
>
>     alias _val this;
> }
>
> alias dollars_t = TypeDef!(uint);
> alias cents_t = TypeDef!(uint);
> ----
>
> Thanks to the second template parameter 'l' the template instances of dollars_t and cents_t aren't equal.

March 14, 2015
On Saturday, 14 March 2015 at 16:55:09 UTC, Charles Cooper wrote:
> Interesting. I think in the second example there are pathological cases where one has similar declarations in two modules at the same line.
Yes, that right, I've kept it simple, but of course it is not complete safe. :)
March 14, 2015
On Saturday, 14 March 2015 at 15:45:30 UTC, Charles Cooper wrote:
> I think I may have answered my own question. It seems std.typecon provides a facility for this.
> http://dlang.org/phobos/std_typecons.html#.Proxy
> http://dlang.org/phobos/std_typecons.html#.Typedef
>
> Is this the 'right' way to do things? It seems that Proxy is used as a mixin whereas Typedef is used to create standalone types.

If memory serves me, Typedef was created to regain this exact feature after "typedef" was deprecated. "typedef" did this while its replacement, "alias", did not and the realization that we had lost that capability led to the creation of Typedef.