Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to simulate "strongdef"?

Tags:

c++

typedef

As you probably know typedef is more like an alias in C++ not a new type, details can be seen here: http://dlang.org/cpptod.html#typedefs
I really dont like the solution proposed in the link, so Im wondering is there a better way ?

like image 452
NoSenseEtAl Avatar asked Sep 20 '12 19:09

NoSenseEtAl


3 Answers

There is only one way to introduce a new type in C++ -with a class keyword (union/struct/class/enum...). There are ways to hide this stuff behind macros and make those new types as intuitively usable as the old ones (see BOOST_STRONG_TYPEDEF), but the fact remains that it's the only way to introduce this new type.

Imagine you had a newtype keyword that makes a strong typedef:

newtype foo = int; // with 'using' alias syntax

How would conversions to and from that type work? With no conversions, you could never assign any value to an object of your new type. Only explicit conversions might seem intuitive, but what if you really want implicit conversions, and still be able to overload? Well, tough luck. You might be able to add all kinds of syntax to leave the decision to the user, but I'm sure you can always come up with corner cases that would need new syntax. Just make it a struct, hide it behind a macro and be done with it.

like image 114
Xeo Avatar answered Nov 20 '22 13:11

Xeo


Generic "no time to make a proper interface" macro:

#define STRONG_CLASS_TYPEDEF(oldType, newType) \
struct newType : private oldType {             \
    template<typename... T>                    \
    newType(T... foo) : oldType(foo...) {}     \
    oldType* operator->() { return this; }     \
    oldType& operator*() {return *this;}       \
};

If your compiler doesn't support variadic templates, you'll need to manually insert whatever constructors you need instead. That is basically a build-your-own inheriting constructor.

Unfortunately I don't think it is possible to restrict the new type implicitly casting to the base, but expose all of its public fields. There are a few workarounds:

You can just using oldType::X; all of the methods and variables you need. This is definitely the best solution, but takes a while to do.

Or use the sneaky arrow operator overload and call foo->method();. Or "dereference" the new strong type into the base type. These are basically just a fancy explicit cast. But considering operator oldType() (explicit or not) doesn't even work with private inheritance...

Anyway, here's a strong type for the std::string and Name example.

struct Name : private std::string {
    template<typename... T>
    Name(T... foo) : std::string(foo...) {}

    std::string* operator->() { return this; }
    const std::string& operator*() {return *this;}
    //The above is obviously a bad idea if you want to use this alongside pointers

    using std::string::resize;
    using std::string::size;
    using std::string::insert;
    using std::string::operator[];
    // etc.
    //Simplest to use, but a bit more to set up
};

Also, you'll have to wrap the non-member operator overloads (std::string == in this example).

bool operator==(Name& lhs, Name& rhs) { return *lhs == *rhs; }
//Note: "Dereference" turns it into a std::string, as above

Fair warning, I haven't tested much beyond basic mechanics. It might add a (very) small amount of overhead. But the effectively empty class is probably optimized away.

like image 2
ECrownofFire Avatar answered Nov 20 '22 14:11

ECrownofFire


Use BOOST_STRONG_TYPEDEF to create "strongdefs" in C++. There is no built-in language feature to do so.

That said it would be interesting to know the real problem you're trying to solve because I've found the need for non-alias typedefs to be very minimal across code I've worked on.

like image 1
Mark B Avatar answered Nov 20 '22 15:11

Mark B