www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - 'strong types' a la boost

reply "Charles Cooper" <chevil gmail.com> writes:
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
Mar 14 2015
parent reply "Charles Cooper" <chevil gmail.com> writes:
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
Mar 14 2015
next sibling parent reply "Namespace" <rswhite4 gmail.com> writes:
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.
Mar 14 2015
parent reply "Charles Cooper" <chevil gmail.com> writes:
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.
Mar 14 2015
parent "Namespace" <rswhite4 gmail.com> writes:
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. :)
Mar 14 2015
prev sibling parent "Brad Anderson" <eco gnuk.net> writes:
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.
Mar 14 2015