Any plans for strong typedef

1,192 views
Skip to first unread message

nx02co...@gmail.com

unread,
Jan 4, 2013, 6:22:36 AM1/4/13
to std-pr...@isocpp.org
Hi, just a quick Q:
Any plans to add to core lang strong typedef(aka it creates new type not an alias of a existing one).

Existing library implementation is BOOST_STRONG_TYPEDEF but it only works for simple(sorry, IDK exact terminology, maybe I should say fundamental ? ) types.

more can be seen here:
http://www.boost.org/doc/libs/1_52_0/libs/serialization/doc/strong_typedef.html

I know std ppl dont like new keywords so maybe you could use something like:

explicit typedef

Klaim - Joël Lamotte

unread,
Jan 4, 2013, 7:04:22 AM1/4/13
to std-pr...@isocpp.org
There have been a big discussion about this, with a paper, the feature being called "opaque typedef".


Joel Lamotte

nx02co...@gmail.com

unread,
Jan 4, 2013, 7:55:27 AM1/4/13
to std-pr...@isocpp.org
seems inconclusive. :D
either way tnx for the link. I hope it will end up in C++17 in some form.

robertmac...@gmail.com

unread,
Jan 8, 2013, 12:20:22 PM1/8/13
to std-pr...@isocpp.org, nx02co...@gmail.com
FWIW I'm the author of BOOST_STRONG_TYPEDEF.

I made this small component in order to make numeric things like integers have different types for template meta programming but still behave like integers by inheriting the behavior of integers - arithmetic operators, implicit conversions etc.

It seemed like a good idea at the time but I eventually came to regret it.  Not there was anything intrinsically wrong with the idea.  But inheritance/conversion of integers create subsequent problems that implicit conversions and and C++ arithmetic do.  So I eventually replaced usage of BOOST_STRONG_TYPEDEF with the idiom:

class class_version {
    unsigned int m_i;
public:
   bool operator==(const class_version & rhs) const;
  // etc.
};

This let me control the usage and trap future errors.  Soooo - I'm really wondering whether adding something more to the language is really a great idea.

Robert Ramey



nx02co...@gmail.com

unread,
Jan 8, 2013, 12:38:43 PM1/8/13
to std-pr...@isocpp.org, nx02co...@gmail.com, robertmac...@gmail.com
Hi, I found your work really helpful when I have functions taking bunch of arguments and some of them are same type...
for example latitude and longitude... it is real pain to remember to pass them in correct order, and bothering with class or std::pair-ing them also feels like overkill.
So if you can I would really like if you could provide some of the real world examples that made you regret making BOOST_STRONG_TYPEDEF

Chris Bush

unread,
Jan 8, 2013, 1:17:29 PM1/8/13
to std-pr...@isocpp.org
I have also been interested in this issue, partially inspired by a
segment of Bjarne Stroustrup's "Going Native" keynote in which he
discusses the programming error at the root of a certain Mars probe's
failure - calculations in one unit system were passed to a function
that expected calculations in another. I think we could all benefit
from a simple way to make differentiated PODs (and, I guess, more
complex types). Now if only coming up with a 'simple solution' were...
well, you know.

One immediate solution I have used was to create a template
`hard_type` which is simply a wrapper that re-implements the operators
exclusively with matching type. In addition to the type it is supposed
to replicate, it takes an number as template argument, which is to be
manually incremented for each differentiation. The C++11 `using` is
used to define the types:

using feet_per_second = hard_type<double,0>;
using meters_per_second = hard_type<double,1>;

This has worked for my little purposes but I can see how it would
quickly become problematic if used in library headers: to boot,
protection would be lost if multiple libraries define a
hard_type<double,0>. Plus who wants to manually keep track of which
number have been used even if all using calls are in the same place.
An internal hash of the new type name might do the trick, but that
would be up to the compiler writer.

Again, the whole purpose of this is to have the compiler reject
mismatched hard types.

Like you, I also had the thought that the keyword `explicit` might be
a good candidate for letting the compiler know that my_type is a T
that is not to be crossed with another T. Something like

using my_type = explicit int;

But whether that's feasible or even practical is beyond my experience.

All of this to say +1 for strong typedefs.

CB

Nevin Liber

unread,
Jan 8, 2013, 2:21:55 PM1/8/13
to std-pr...@isocpp.org
On 8 January 2013 12:17, Chris Bush <chr...@gmail.com> wrote:
I think we could all benefit
from a simple way to make differentiated PODs (and, I guess, more
complex types).

I'm not sure I'd try anything more than fundamental types.
 
One immediate solution I have used was to create a template
`hard_type` which is simply a wrapper that re-implements the operators
exclusively with matching type.

Does it do so using Boost.Operator Type Traits <http://www.boost.org/doc/libs/1_52_0/libs/type_traits/doc/html/boost_typetraits/category/value_traits/operators.html>?  That would be interesting...
 
In addition to the type it is supposed
to replicate, it takes an number as template argument, which is to be
manually incremented for each differentiation.

You would be better off using a type tag; that way, you don't need a centralized place to manage the count.


The hard question is: what operations are allowed across types and what aren't?  For instance, is it possible to legally convert a my_type* into an int*?  If you move to user defined types, that question becomes nearly impossible to answer.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

s.h...@oisyn.nl

unread,
Jan 8, 2013, 5:54:05 PM1/8/13
to std-pr...@isocpp.org


On Tuesday, January 8, 2013 7:17:29 PM UTC+1, C Bush wrote:
In addition to the type it is supposed
to replicate, it takes an number as template argument, which is to be
manually incremented for each differentiation. The C++11 `using` is
used to define the types:

    using feet_per_second = hard_type<double,0>;
    using meters_per_second = hard_type<double,1>;

This has worked for my little purposes but I can see how it would
quickly become problematic if used in library headers: to boot,
protection would be lost if multiple libraries define a
hard_type<double,0>. Plus who wants to manually keep track of which
number have been used even if all using calls are in the same place.

But that's easily solvable. Drop the non-type template parameter and add a type parameter.

using feet_per_second = hard_type<double, struct feet_per_second_tag>;
using meters_per_second = hard_type<double, struct meters_per_second_tag>;

A workable solution, but suffice to say I'd prefer a language feature as well

- Sylvester

Chris Bush

unread,
Jan 11, 2013, 1:14:27 AM1/11/13
to std-pr...@isocpp.org
On Tue, Jan 8, 2013 at 2:21 PM, Nevin Liber <ne...@eviloverlord.com> wrote:
>
> You would be better off using a type tag; that way, you don't need a
> centralized place to manage the count.

+

On Tue, Jan 8, 2013 at 5:54 PM, <s.h...@oisyn.nl> wrote:
>
> But that's easily solvable. Drop the non-type template parameter and add a
> type parameter.
>
> using feet_per_second = hard_type<double, struct feet_per_second_tag>;
> using meters_per_second = hard_type<double, struct meters_per_second_tag>;
>
> A workable solution, but suffice to say I'd prefer a language feature as
> well
>
> - Sylvester
>

Type tags. Thanks for that tip!



On Tue, Jan 8, 2013 at 2:21 PM, Nevin Liber <ne...@eviloverlord.com> wrote:
>> One immediate solution I have used was to create a template
>> `hard_type` which is simply a wrapper that re-implements the operators
>> exclusively with matching type.
>
>
> Does it do so using Boost.Operator Type Traits
> <http://www.boost.org/doc/libs/1_52_0/libs/type_traits/doc/html/boost_typetraits/category/value_traits/operators.html>?
> That would be interesting...


Regrettably no.


> The hard question is: what operations are allowed across types and what
> aren't? For instance, is it possible to legally convert a my_type* into an
> int*? If you move to user defined types, that question becomes nearly
> impossible to answer.

Let's focus on simple types, then.

I would suggest that this pointer conversion, without a cast, should
be impossible because my_type is not a derived type (which would allow
for such a conversion due to polymorphism) but is rather meant to be a
distinct type. As you can't convert e.g. a short* to an int* without a
cast, neither should you be able to convert my_type* to int* even
though my_type is based on int. But this distinction can be cast away
so as not to hinder or limit the conscientious programmer.

People using this hypothetical feature will expect their functions
that specifically request a my_type* to guarantee that no other
int-type pointer makes its way in unless someone *really* went out of
their way to mess it up.


Thoughts?

CB

robertmac...@gmail.com

unread,
Jan 11, 2013, 12:38:14 PM1/11/13
to std-pr...@isocpp.org, nx02co...@gmail.com, robertmac...@gmail.com


On Tuesday, January 8, 2013 9:38:43 AM UTC-8, nx02co...@gmail.com wrote:
Hi, I found your work really helpful when I have functions taking bunch of arguments and some of them are same type...
for example latitude and longitude... it is real pain to remember to pass them in correct order, and bothering with class or std::pair-ing them also feels like overkill.
So if you can I would really like if you could provide some of the real world examples that made you regret making BOOST_STRONG_TYPEDEF


Regret is too strong.  It was convenient at the time but I later found that it had some downsides so I backed it out.  Note that this occurred in a number of places in the serialization library.  That is, one has to "get it done" and which is a huge job.  Later when more time is available, one can go back and fix bugs/complaints by re-implementing some things.

Here's the case with  BOOST_STRONG_TYPEDEF.  I have a "special" kind of integer.  For example a class version number.  This is a number well modeled by an integer.  But I want to maintain it as a separate type so that overload resolution and specialization can depend on the type. I needed this in a number of instances and so rather than re-implementing it every time I make BOOST_STRONG_TYPEDEF.  This leveraged on another boost library which implemented all the arithmetic operations so I could derive from this.  So it was only a few lines of macro code and imported all the "numeric" capabilities via inheritance.  Just perfect.

But in time I came to appreciate that the things I was using it for weren't really normal numbers.  It makes sense to increment a class version number - but it doesn't make any sense to multiply two class version numbers.  Does it make sense to automatically convert a class version number to an unsigned int?  Hmmmm - it seemed like a good idea a the time, but it introduced some very sticky errors in the binary_?archive.  So I realized that what I really needed was more specific control over the numeric operations rather than just inheritiing the whole set for integers.  So I evolved away from BOOST_STRONG_TYPEDEF.

No question that BOOST_STRONG_TYPEDEF has value and is useful.  But it's also not the whole story. It's more of a stepping stone along the way to more perfect code.  Most programs stop evolving before it gets to the point where one want's to go back and make this more explicit.  The serialization library is somewhat different because under pressure of users to make it more bug free and bullet proof, I was motivated to evolve/enhance things longer than one might so for other types of projects.

Robert Ramey

vven...@gmail.com

unread,
Dec 10, 2014, 5:14:37 PM12/10/14
to std-pr...@isocpp.org, nx02co...@gmail.com, robertmac...@gmail.com
Robert , 
Why typedef T type 

was not included in your implementation, when using BOOST_STRONG_TYPDEF with templates it's not possible to figure out underlying type.
Say , you are writing a something combined with boost::hash<T>

I though it would be nice to do like 

BOOST_STRONG_TYPDEF(std::string, MyType)

Test<MyType> test; // it takes MyType as template parameter
....
.....
.....

template <typename boost_strong_type>
struct Test {
//it would be nice if besides exposing T _t  in BOOST_STRONG_TYPEDEF macro , the macro would also create and alias for T with typedef T type;
typedef boost::unorteder_set <MyType , boost::hash<boost_strong_type::type>  >  my_map_t ; 
...
...
}





Thanks,
Vlad.

vven...@gmail.com

unread,
Dec 10, 2014, 5:32:43 PM12/10/14
to std-pr...@isocpp.org, nx02co...@gmail.com, robertmac...@gmail.com, vven...@gmail.com
I meant to say :

template <typename boost_strong_type>
struct Test {
//it would be nice if besides exposing T _t  in BOOST_STRONG_TYPEDEF macro , the macro would also create and alias for T with typedef T type;
typedef boost::unorteder_set <boost_string_type , boost::hash<boost_strong_type::type>  >  my_map_t ; 
...
...

german...@personifyinc.com

unread,
Dec 10, 2014, 9:38:02 PM12/10/14
to std-pr...@isocpp.org, nx02co...@gmail.com


On Friday, January 4, 2013 6:22:36 PM UTC+7, nx02co...@gmail.com wrote:
Hi, just a quick Q:
Any plans to add to core lang strong typedef(aka it creates new type not an alias of a existing one).

My question is: if we can finally overload operator dot, would we need a specific mechanism for strong typedefs?


 

Vicente J. Botet Escriba

unread,
Dec 11, 2014, 2:01:47 AM12/11/14
to std-pr...@isocpp.org
Le 11/12/14 03:38, german...@personifyinc.com a écrit :
My question would be: do we want to make much complex than needed the definition of a strong type?
Maybe with overload operator dot, the strong type alias would be only syntactic sugar.

Vicente

german...@personifyinc.com

unread,
Dec 11, 2014, 2:09:44 AM12/11/14
to std-pr...@isocpp.org
Well, designing a specific feature that goes into core, if we have the tools availabel (operator dot) maybe a lib solution (template with policies?)
is better. You don't marry to a template, but you marry the compiler to a language feature. Besides that, it is easier
to make a feature into the lib than into core, I guess, for the marriage reason :)




Vicente J. Botet Escriba

unread,
Dec 11, 2014, 2:24:06 AM12/11/14
to std-pr...@isocpp.org
Le 11/12/14 08:09, german...@personifyinc.com a écrit :
Agreed completely. I didn't said that it was a simple task to introduce a new core feature to the standard. What I'm asking is if we want that users use simple things via simple syntax.


Vicente

Vicente J. Botet Escriba

unread,
Dec 11, 2014, 2:24:06 AM12/11/14
to std-pr...@isocpp.org
Le 11/12/14 08:09, german...@personifyinc.com a écrit :

vven...@gmail.com

unread,
Dec 11, 2014, 7:57:37 PM12/11/14
to std-pr...@isocpp.org
#define BOOST_STRONG_TYPEDEF2(T, D) \
struct D \
: boost::totally_ordered1< D \
, boost::totally_ordered2< D, T \
> > \
{ \
typedef T type \
T t; \
explicit D(const T t_) : t(t_) {}; \
D(){}; \
D(const D & t_) : t(t_.t){} \
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
template<typename U> \
typename boost::enable_if<boost::is_same<U,T>, D>::type & \
operator=(const U & rhs ) { t = rhs; return *this;} \
operator const T & () const {return t; } \
operator T & () { return t; } \
bool operator==(const D & rhs) const { return t == rhs.t; } \
bool operator<(const D & rhs) const { return t < rhs.t; } \
};


1.) nice to have typdef of actual type and 2.) also your strong type converts between each other strong type if underlying type is the same:

BOOST_STRONG_TYPEDEF2(long, EntityId)
BOOST_STRONG_TYPEDEF2(long, AcctNo)


int main(int argc, char** argv) {
AcctNo acct_no;
EntityId id ;
acct_no = id ; //<-- original original implementation allows conversion , however see what can be achieved with SFINAE in my version
return 0;
}

vven...@gmail.com

unread,
Dec 11, 2014, 8:02:31 PM12/11/14
to std-pr...@isocpp.org
proposing to change BOOST_STRONG_TYPEDEF's operator :

D & operator=(const T & rhs) { t = rhs; return *this;}


to

template<typename U>
typename boost::enable_if<boost::is_same<U,T>, D>::type &
operator=(const U & rhs ) { t = rhs; return *this;}


This will prevent from conversion of different strong types with same underlying type!!!

vven...@gmail.com

unread,
Dec 11, 2014, 9:50:26 PM12/11/14
to std-pr...@isocpp.org
D(){};


should be fixed to

D() : t() {};


in order for underlying primitives types to be default initialized, because compiler does not initialize them it.

vven...@gmail.com

unread,
Dec 11, 2014, 10:01:25 PM12/11/14
to std-pr...@isocpp.org
Actually I take it back , as it forces for non-pod types of 't' to have default ctor , my bad sorry

vven...@gmail.com

unread,
Dec 12, 2014, 1:59:43 AM12/12/14
to std-pr...@isocpp.org
Are you maintaining your macro in boost ? If
So can I propose to fix it ? so that it won't be able to assign one strong type to another having same underlying type .
Please reply if patch can be released , see my posts bow

Vicente J. Botet Escriba

unread,
Dec 12, 2014, 4:25:09 PM12/12/14
to std-pr...@isocpp.org
Le 12/12/14 07:59, vven...@gmail.com a écrit :
Are you maintaining your macro in boost ? If 
So can I propose to fix it ? so that it won't be able to assign one strong type to another having same underlying type .
Please reply if patch can be released , see my posts bow 

Please, switch to the Boost ML for patches for Boost.

Vicente

Robert Ramey

unread,
Mar 13, 2015, 3:21:05 PM3/13/15
to std-pr...@isocpp.org


On Tuesday, January 8, 2013 at 10:17:29 AM UTC-8, C Bush wrote:
I have also been interested in this issue, partially inspired by a
segment of Bjarne Stroustrup's "Going Native" keynote in which he
discusses the programming error at the root of a certain Mars probe's
failure - calculations in one unit system were passed to a function
that expected calculations in another. I think we could all benefit
from a simple way to make differentiated PODs (and, I guess, more
complex types). Now if only coming up with a 'simple solution' were...
well, you know.

As far as I'm concerned, the definitive solution for this is boost units library. Here are a few observations on it.

a) The tutorial documentation is terrible
b) the reference documentation is not much better 
c) The above required me spend more than a few days spelunking the documentation, examples, library code, etc.
d) It's very wide ranging and includes almost everything
e) Implemenation is an incredible piece of template meta program
f) has worked very well for me.

Sooooo - great software - which a huge hurdle to is usage.  

If anyone cares - I may well be giving  a tutorial introduction to this library at CPP Con 2015.

Robert Ramey

Sean Middleditch

unread,
Mar 14, 2015, 2:44:07 AM3/14/15
to std-pr...@isocpp.org
On Friday, March 13, 2015 at 12:21:05 PM UTC-7, Robert Ramey wrote:
d) It's very wide ranging and includes almost everything
e) Implemenation is an incredible piece of template meta program

This is what makes it almost a complete non-starter. Including a few tens or hundreds of thousands of lines of complicated templates that must be instantiated just to get the compiler to avoid implicitly copying/casting between a couple of "different" integers or simple PODs is utterly unacceptable to a great number of key C++ users.

Many game companies can (and very, very often do) just prefer C99 over this kind of massive bloating. Using Boost for opaque typedefs is not unlike using boost::bind for lambdas; it's a neat proof of concept, but ultimately something the compiler needed to just do itself. It's not just the syntax, or the incomprehensible template errors of the library solution, or the slower compilation times of a pile of templates bigger and more complicated than the program itself, or the wizards-only internals, but a confluence of these issues.

Jonathan Coe

unread,
Mar 14, 2015, 6:29:58 AM3/14/15
to std-pr...@isocpp.org
+1


--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
Reply all
Reply to author
Forward
0 new messages