Darius Duesentrieb
Darius Duesentrieb

Reputation: 847

C++: How to duplicate a datatype?

I want to use a datatype with a different name (create a duplicate of a type). I don't want to use 'typedef' since that only creates a #define/macro like an alias.

#include <iostream>

typedef int AnInt;

struct Number
{
    int a;
};

template<typename T>
T var;

int main()
{
    var<int> = 5;
    var<AnInt> = 7; // does not what i want (this changes var<int>)
    var<Number>.a = 7;
    return 0;
}

This works exactly how I want it to work but I always need to access the type with the postfix .a. Is there a way to avoid this?

Edit:

The real-world application is that I have a vec3 datatype and now I need to different datatypes Position and Velocity that are essentially a single vec3. They need to be different because I use an entity-component system that is based on templates.

Upvotes: 1

Views: 102

Answers (3)

Useless
Useless

Reputation: 67733

The simplest way to create a new, non-implicitly-convertible type with exactly the same layout as the original is inheritance:

struct Position: vec3 {};
struct Velocity: vec3 {};

Note that they're still implicitly convertible to vec3d&, just not to each other.

Upvotes: 3

Innokentiy Alaytsev
Innokentiy Alaytsev

Reputation: 840

If you want to ensure type safety try foonathan/type_safe. This is a header-only library that provides mechanisms for opaque typedef emulation using standard C++:

type_safe provides zero overhead abstractions that use the C++ type system to prevent bugs.

Zero overhead abstractions here and in following mean abstractions that have no cost with optimizations enabled, but may lead to slightly

lower runtime in debug mode, especially when assertions for this library are enabled.

The library features cannot really explained in the scope of this readme, I highly suggest that you check out the first and second blog post and the examples.

If you only need to use strong typedefs for something that involves physics, have a look at Boost.Units:

The Boost.Units library is a C++ implementation of dimensional analysis in a general and extensible manner, treating it as a generic compile-time metaprogramming problem. With appropriate compiler optimization, no runtime execution cost is introduced, facilitating the use of this library to provide dimension checking in performance-critical code. Support for units and quantities (defined as a unit and associated value) for arbitrary unit system models and arbitrary value types is provided, as is a fine-grained general facility for unit conversions. Complete SI and CGS unit systems are provided, along with systems for angles measured in degrees, radians, gradians, and revolutions and systems for temperatures measured in Kelvin, degrees Celsius and degrees Fahrenheit. The library architecture has been designed with flexibility and extensibility in mind; demonstrations of the ease of adding new units and unit conversions are provided in the examples.

P.S. If you would like some code examples then let me know.

Upvotes: 0

IS4
IS4

Reputation: 13187

Since typedef simply creates an alias (not a macro though) for the type, you need to create a new type to represent its new identity. Since you want the access to the value to be as smooth as possible, you can define a new class and overload some operators:

template <class T>
class wrapper
{
    T value;
public:
    wrapper()
    {

    }

    wrapper(T &&obj) : value(std::move(obj))
    {

    }

    wrapper(const T &obj) : value(obj)
    {

    }

    operator T&()
    {
        return value;
    }

    operator const T&() const
    {
        return value;
    }

    T &operator*()
    {
        return value;
    }

    const T &operator*() const
    {
        return value;
    }

    const T &operator->() const
    {
        return value;
    }

    T &operator->()
    {
        return value;
    }
};

Add more operators, if necessary. Then, each new type shall inherit from this class like this:

struct new_int : public wrapper<int>
{
    new_int()
    {

    }

    new_int(int &&obj) : wrapper<int>(std::move(obj))
    {

    }

    new_int(const int &obj) : wrapper<int>(obj)
    {

    }
};

You can create a macro that helps with constructing these types:

#define new_type(name, base) struct name : public wrapper<base> { \
    name() {} \
    name(base &&obj) : wrapper<base>(std::move(obj)) {} \
    name(const base &obj) : wrapper<base>(obj) {} \
}

All these new types will be distinct from each other:

new_type(new_int1, int);
new_type(new_int2, int);

int main()
{
    var<int> = 12;
    var<wrapper<int>> = 13;
    var<new_int1> = 14;
    var<new_int2> = 15;
    std::cout << var<int> << std::endl;
    std::cout << var<wrapper<int>> << std::endl;
    std::cout << var<new_int1> << std::endl;
    std::cout << var<new_int2> << std::endl;
}

Upvotes: 2

Related Questions