peoro
peoro

Reputation: 26060

Differentiate typedefs

I'm writing a C++ abstraction for a C library. The C library has several typedefs for IDs that identify remote resources:

typedef int color_id;
typedef int smell_id;
typedef int flavor_id;
// ...

color_id createColor( connection* );
void destroyColor( connection*, color_id );
// ...

All these typedefs are of course the same type in the compiler's eyes. This is a problem for me, because I would like to overload functions and specialize templates to offer a nice C++ friendly API:

// can't do the following since `color_id`, `smell_id` and `int` are the same
std::ostream& operator<<( std::ostream&, color_id );
std::ostream& operator<<( std::ostream&, smell_id );
void destroy( connection*, color_id );
void destroy( connection*, smell_id );

// no static check can prevent the following
smell_id smell = createSmell( connection );
destroyColor( connection, smell ); // it's a smell, not a color!

Since I don't know of any other way, I've been thinking about creating a different wrapper type for each C type. But this path seems quite rough...

  1. There's a lot of code out there already specialized for primitive types (e.g. std::hash).
    Is there any way to tell the compiler something like "if something has a specialization for int, but not for my wrapper, then just use the int specialization"?
    Should I otherwise write specializations for stuff like std::hash? What about similar templated structs that are not in std (e.g. stuff in boost, Qt etc)?

  2. Should I go with implicit or explicit constructor and casting operator? Explicit ones are of course safer, but they would make it very tedious to interact with existing code and third party libraries that use the C API.

I'm more than open to any tips from whoever has already been there!

Upvotes: 5

Views: 229

Answers (2)

Ant
Ant

Reputation: 1708

Use BOOST_STRONG_TYPEDEF as commented by @MooingDuck.

Upvotes: 0

Alecto
Alecto

Reputation: 10740

One wrapper class to rule them all

Your best bet would be to create a wrapper class, but using templates we can write one wrapper class template and use it for all the different IDs just by assigning them to different instances of the template.

template<class ID> 
struct ID_wrapper
{
    constexpr static auto name() -> decltype(ID::name()) {
        return ID::name(); 
    }
    int value;

    // Implicitly convertible to `int`, for C operability
    operator int() const {
        return value; 
    }  
};

Overloading std::hash (just once)

We can stick whatever traits we want in the ID class, but I provided name() as an example. Since ID_Wrapper is written as a template, specializing it for std::hash and other classes only has to be done once:

template<class ID>
class std::hash<ID_wrapper<ID>> : public std::hash<int>
{
   public:
    // I prefer using Base to typing out the actual base
    using Base = std::hash<int>;

    // Provide argument_type and result_type
    using argument_type = int;
    using result_type = std::size_t; 

    // Use the base class's constructor and function call operator
    using Base::Base; 
    using Base::operator(); 
};

Printing out an ID with it's name

If you want, we could also specialize operator<<, but ID_wrapper is implicitly convertible to an int anyways:

template<class ID>
std::ostream& operator<<(std::ostream& stream, ID_Wrapper<ID> id) {
    stream << '(' << ID_Wrapper<ID>::name() << ": " << id.value << ')'; 
    return stream; 
}

Once we have that, we just write a traits class for each ID type!

struct ColorIDTraits {
    constexpr static const char* name() {
        return "color_id";
    }
};

struct SmellIDTraits {
    constexpr static const char* name() {
        return "smell_id";
    }
};

struct FlavorIDTraits {
    constexpr static const char* name() {
        return "flavor_id";
    }
};

Wrapping it all together

We can then typedef the ID_wrapper:

using color_id = ID_wrapper<ColorIDTraits>;
using smell_id = ID_wrapper<SmellIDTraits>;
using flavor_id = ID_wrapper<FlavorIDTraits>;

Upvotes: 3

Related Questions