5

Consider a class template and auxiliary enum classes defined as follows:

enum class Color {Red, Green, Blue}
enum class ShowAxes {False, True}
enum class ShowLabels {False, True}

template< Color, ShowAxes, ShowLabels >
class A
{......};

The question is, how to redefine the class A, which would be independent on the permutations of its arguments. I use Dev C++, which supports C++11.

[EDIT]

For example, the new version of A should support

A< Color::Red, ShowAxes::True, ShowLabels::True >
A< Color::Red, ShowLabels::True, ShowAxes::True >
A< ShowAxes::True, Color::Red, ShowLabels::True >
A< ShowLabels::True, Color::Red, ShowAxes::True >
A< ShowLabels::True, Color::Red, ShowAxes::True >
A< ShowAxes::True, Color::Red, ShowLabels::True >

versions, and all of them are identical, i.e. they generate the same class.

2 Answers2

5

It isn't possible with your current interface using non-type parameters.

You can take type parameters instead and wrap the values in a std::integral_constant:

template<class X, class Y, class Z>
class A { /* stuff */ };

// use as:
A<std::integral_constant<Color, Color::Red>,
  std::integral_constant<ShowAxes, ShowAxes::True>,
  std::integral_constant<ShowLabels, ShowLabels::True>> a;

This is rather verbose, so you could consider writing a macro:

#define AS_IC(Value) std::integral_constant<decltype(Value), Value>

and rewrite as

A<AS_IC(Color::Red), AS_IC(ShowAxes::True), AS_IC(ShowLabels::True)> a;

Extracting the value of the desired type from the list of integral_constants is straightforward:

template<class Result, class...>
struct extract;

template<class Result, Result Value, class... Tail>
struct extract<Result, std::integral_constant<Result, Value>, Tail...> : std::integral_constant<Result, Value> {};

template<class Result, class Head, class... Tail>
struct extract<Result, Head, Tail...> : extract<Result, Tail...> {};

Then you can do

// inside the definition of A
static constexpr Color col = extract<Color, X, Y, Z>::value;

Demo.

This do not, however, generate the same class, but you can make a class template A_impl that behaves like your A with non-type parameters, and that contains the actual implementation, and then make A an alias template:

template< Color, ShowAxes, ShowLabels >
class A_impl
{/* stuff */};

template<class X, class Y, class Z>
using A = A_impl<extract<Color, X, Y, Z>::value,
                 extract<ShowAxes, X, Y, Z>::value,
                 extract<ShowLabels, X, Y, Z>::value>;

Now given

A<AS_IC(Color::Red), AS_IC(ShowAxes::True), AS_IC(ShowLabels::True)> a;
A<AS_IC(Color::Red), AS_IC(ShowLabels::True), AS_IC(ShowAxes::True)> b;

a and b have the same type. Demo.

In the alternative, you can also use decltype and overloading function templates, but that requires adding a function template declaration for every possible order of types:

template< Color c, ShowAxes a, ShowLabels l>
A<c,a,l> A_of();

template< ShowAxes a, ShowLabels l, Color c>
A<c,a,l> A_of();

// etc.

decltype(A_of<Color::Red, ShowAxes::True, ShowLabels::True>()) a1;
decltype(A_of<ShowAxes::True, ShowLabels::True, Color::Red>()) a2;
T.C.
  • 133,968
  • 17
  • 288
  • 421
  • The idea of `std::integral_constant` and `extract` looks nice. I will think about optimization of the code. – Vahagn Poghosyan Feb 02 '15 at 12:12
  • I have a small (maybe trivial) question. Is it possible to replace a macro AS_IC by a non-directive staff, say a class template or template function ? – Vahagn Poghosyan Feb 02 '15 at 15:01
  • 1
    @VahagnPoghosyan No macro-free way that I'm aware of without adding lots of boilerplate when you declare an `A`. – T.C. Feb 03 '15 at 01:03
0

Maybe by using std::is_same. Then you can simplify your code this way:

template <typename A, typename B, typename C>
class X
{
public:
    X() {
        static_assert(
            std::is_same<A, Color>::value ||
            std::is_same<B, Color>::value ||
            std::is_same<C, Color>::value,
            "nope");
        // other assertions here!
        // also, make sure your types are different ;)
    }
    X(A a, B b, C c) : X() {
        // your code here
    }
};

template <typename A, typename B, typename C>
X<A, B, C> make(A a, B b, C c) {
    // possible verifications here
    return X<A, B, C>(a, b, c);
}

int main() {
    auto a = make(Color::Red, ShowAxes::true, ShowLabels::True);
    return 0;
}

You can verify all of your types A, B & C.

I'm sorry, but I don't see any other solution. :/

vincentp
  • 1,433
  • 9
  • 12
  • I added some text in the question for the detailed explanation of my aim. – Vahagn Poghosyan Feb 02 '15 at 11:21
  • Thank you, but for example `make(Color::Red, ShowAxes::True, ShowLabels::True)` and `make(ShowAxes::True, Color::Red, ShowLabels::True)` give different results. They should generate the same class. – Vahagn Poghosyan Feb 02 '15 at 11:51
  • Can inheritance resolve the problem? If the X class inherits from a Base class, they can be of the same type thanks to polymorphism. – vincentp Feb 02 '15 at 11:54