15

I would like to use something like typedef in my C++ programs to enhance type safety.

As an example, suppose I have two functions

void function1(unsigned idOfType1);
void function2(unsigned idOfType2);

then I can mistakenly pass idOfType2 to function1 and vice versa. I want the compiler to give me an error in this case. I am aware that I could wrap these unsigned in a struct, but then I'd have to give provide a field name and use . to access them, which is slightly inconvenient. Is there a good way around this?

Edit: As far as I know typedef will not work for this purpose as it is just a shorthand for a type and will not be used for type checking.

Paul
  • 7,836
  • 2
  • 41
  • 48
  • 2
    Enums work well in your case... – Alexander Pavlov Mar 06 '12 at 16:44
  • @AlexanderPavlov care to elaborate, it is not obvious to me ... – Paul Mar 06 '12 at 16:47
  • 2
    @Paul: you can write something like `enum Type1 { dummy0 = 1, dummy1 = 1<<1, dummy2 = 1<<2, ..., dummy31 = 1<<31};`, and assuming `unsigned` is a 32 bit type on your implementation then the result is an enum that can hold any value of `unsigned`. Then `void function1(Type1 id)` won't accept a `Type2`, because enums aren't implicitly convertible to each other. – Steve Jessop Mar 06 '12 at 16:49
  • @SteveJessop: right, I meant a similar approach but not with the powers-of-two values - Paul is not going to bitwise-or them, does he? – Alexander Pavlov Mar 06 '12 at 16:52
  • @SteveJessop I see, thanks for the explanation. It feels a little hackish, though. How would conversion from and two integers work? – Paul Mar 06 '12 at 16:54
  • @Alexander: I don't know what an `idOfType1` actually is, but it may not be something that can be enumerated in source. Suppose it's some kind of unique ID for a domain entity, like an incrementing primary key from a DB? Come to think of it, you do only need `dummy0 = UINT_MAX`, not sure what I was thinking with the other values! – Steve Jessop Mar 06 '12 at 16:55
  • @SteveJessop Yes, it's a unique ID comming from a DB. – Paul Mar 06 '12 at 16:56
  • @Paul: then this approach won't work for you :( – Alexander Pavlov Mar 06 '12 at 16:57
  • http://www.boost.org/doc/libs/1_61_0/doc/html/boost_units.html is something that may interest you. – n. m. could be an AI Sep 21 '16 at 14:54

6 Answers6

15

Use Boost strong typedef:

typedef creates an alias for an existing type. It does not create a new type that can be used for matching either function or template parameters...

Usage of BOOST_STRONG_TYPEDEF addresses this...

BOOST_STRONG_TYPEDEF is a macro which generates a class named "name" wraps and instance of its primitive type and provides appropriate conversion operators in order to make the new type substitutable for the one that it wraps.

gnat
  • 6,213
  • 108
  • 53
  • 73
Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
7

As you say, a typedef won't help you here. I can't think of a better way immediately, however if you go with your wrapping in a struct/class option you could use a conversion operator to eliminate the member method or function call.

For example:

struct WrappedType
{
    operator type()
    {
         return _value;
    }

    type _value;  
}

I'm not saying this is the way to do it mind you ;-)

Konrad
  • 39,751
  • 32
  • 78
  • 114
  • 1
    can you show me how I could use the type operator? Boost strong typedef looks like a good option in general, but might not work in my case, so I am interested in this solution! – Paul Mar 06 '12 at 16:49
  • 1
    @Konrad great! Thanks for elaborating. If I can't use boost strong typedef then I will use this solution. – Paul Mar 06 '12 at 16:58
  • 1
    @Konrad I like this answer as it gave me insight into the type operator I was previously unaware of, so I am going to accept you. – Paul Mar 06 '12 at 17:07
  • It's one of those funny things in C++ you don't normally encounter :-) – Konrad Mar 06 '12 at 17:08
  • @Paul: In your code it would be `operator unsigned()`. Don't be surprised when literally typing `operator type()` doesn't work :D – Mooing Duck Mar 04 '14 at 02:49
1

This is a late reply to an old question. But there are new developments on the C++ front and for the sake of completeness I'm adding this answer:

The opaque_typedef library is the author's attempt to provide most of the value of opaque typedefs through a library, without waiting for opaque typedefs to become a language feature.

The author of this library, Kyle Markley had a short brilliant speech at the cppcon 2015 introducing this library. The slides of his speech are on github the source code of the library is available on sourceforge. The library is header-only, written in C++11. Gcc and clang are ok, but VS2015 seems to have problems with it.

The use of the library is straight-forward. The following code was taken from the documentation. It creates an opaque typedef of int. It has the same interface as an int (it can be added, shifted, incremented, compared, etc.) but the arguments and return values are of the newly-created type, not of int:

#include "opaque/numeric_typedef.hpp"

struct myint : opaque::numeric_typedef<int, myint> {
  using base = opaque::numeric_typedef<int, myint>;
  using base::base;
};
user23573
  • 2,479
  • 1
  • 17
  • 36
0

In foonathan's blog post from 2016 various approaches are covered, starting with an example class for simple cases:

class meter
{
public:
    explicit meter(int val)
    : value_(val) {}

    explicit operator int() const noexcept
    {
        return value_;
    }

private:
    int value_;
};

and ending with a short template library with arithmetic support for custom type-safe types, allowing one to write this:

struct meter
: strong_typedef<meter, int>, addition<meter>
{
    using strong_typedef::strong_typedef;
};
ash108
  • 1,763
  • 1
  • 17
  • 21
0

There is a c++11 feature called enum class, which is basically a type safe enum. Maybe they can help here.

ebutusov
  • 563
  • 2
  • 5
-2

You can check the type in your function, so that if it didn't match, you can print an error or something.

You can use typeid to detect variable type, as follows:

typeid(*variablename*).name()

As suggested in one of the answers here, this is compiler-dependent and you have to use try-and-error method to find out which value works for you.

Community
  • 1
  • 1
MMS
  • 408
  • 2
  • 5
  • 9
  • 2
    But then I would have to do that in __every__ function call + it's compiler dependent? sorry, I don't think that this is a good choice... – Paul Mar 06 '12 at 17:59
  • Doesn't work anyway - the type of the parameter `idOfType1` in `function1` is always `unsigned`. What was passed by the caller has already been converted. – Steve Jessop Mar 06 '12 at 18:15