digitalmars.D.learn - 'strong types' a la boost
- Charles Cooper (18/18) Mar 14 2015 I was wondering what the idiomatic D way of implementing strong
- Charles Cooper (9/27) Mar 14 2015 I think I may have answered my own question. It seems std.typecon
- Namespace (41/41) Mar 14 2015 You can do it this way:
- Charles Cooper (23/64) Mar 14 2015 Interesting. I think in the second example there are pathological
- Namespace (3/6) Mar 14 2015 Yes, that right, I've kept it simple, but of course it is not
- Brad Anderson (5/12) Mar 14 2015 If memory serves me, Typedef was created to regain this exact
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
I think I may have answered my own question. It seems std.typecon provides a facility for this. 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
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
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
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
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. 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