Give a generic enum type as template argument
Asked Answered
C

1

11

In short:

Is there a way I can feed a General templated class with something that only represent an enum type? Something like:

template <typename T> struct General {};
struct EnumSpecific : General<any_enum_type> {};

<int> is too much / does not work in my case.


My specific case:

  • A templated Holder class handles any kind of data in a generic way.
  • An abstract General class implements specific algorithms relying on the Holders' behaviour.
  • Template specifications of General (like IntSpecific, DoubleSpecific, StringSpecific, MoreSophisticatedTypeSpecific ..) define how to deal with some concrete Holder types.
  • How can I properly define an EnumSpecific specification?

Here is boilt down code making my problem occur:

// A templated value holder:
template <typename T>
class Holder {
public:
    Holder(T const& t) : _value(t) {};
    // generic methods
    void generics() {};
    // methods concerning the value:
    void set(T const& t /*, setInfo */) {
        // .. check for an actual change, notify buddies of the change..
        _value = t;
    };
    T value(/*readInfo*/) {
        // .. do stuff depending on how / why the value is read..
        return _value;
    };
private:
    T _value;
};
// (in reality, all `generics` methods come from a parent, untemplated class)

// A generic process involving such `Holder`s:
template <typename T>
class General {
public:
    typedef bool /* or anything */ KnownReturnTypes;
    General(Holder<T>* const a
          , Holder<T>* const b)
        : _a(a)
        , _b(b)
    {};
    void methods() {
        // Use common behavior of all `Holder`'s
        _a->generics();
        // .. or methods that rely on the actual values:
        KnownReturnTypes knr( valuedMethods() );
        if (knr) {} else {}
        // ...
    };
    // Use polymorphism to adapt to each situation..
    virtual KnownReturnTypes valuedMethods() = 0;
protected:
    Holder<T>* _a;
    Holder<T>* _b;
};

// Example of specialization for integral types (there might be others)
class IntSpecific : General<int> {
public:
    IntSpecific(Holder<int>* const a
              , Holder<int>* const b)
        : General<int>(a, b)
    {};
    // implement the valuedMethods:
    virtual KnownReturnTypes valuedMethods() {
        return _a->value() > _b->value(); // dummy
    }
};

// Specialization for enum types:
// * * * class EnumSpecific : General<any_enum_type> { // does not exist * *
class EnumSpecific : General<int> {
public:
    EnumSpecific( Holder<int>* const a
                , Holder<int>* const b)
        : General<int>(a, b)
    {};
    // only use properties and methods offered by an enum type:
    virtual KnownReturnTypes valuedMethods() {
        return _a->value() == _b->value(); // dummy
    }
};

// One particular case
typedef enum {One, Two, Three} Enum;
typedef Holder<Enum> EnumHolder;


int main() {

    // Check that `IntSpecific` works fine.
    Holder<int>* i( new Holder<int>(3) );
    Holder<int>* j( new Holder<int>(5) );
    IntSpecific is(i, j); // ok.

    // Try the `EnumSpecific`
    EnumHolder* a( new EnumHolder { One } );
    EnumHolder* b( new EnumHolder { Two } );
    EnumSpecific es(static_cast<Holder<int>*>(a)    // invalid cast
                  , static_cast<Holder<Enum>*>(b)); // unexpected type
    // This is because the compiler doesn't know enough about what
    // EnumSpecific actually *is*. How to tell him more about it?


    return EXIT_SUCCESS;
}

What should I feed the template argument with in EnumSpecific : General<??> to make things clear for the compiler?

Do I need to use some kind of enum_type concept and more sophisticated tools from generic programming?

Calica answered 31/8, 2015 at 17:40 Comment(3)
C++11 gives you std::is_enum: en.cppreference.com/w/cpp/types/is_enumShuffleboard
@AlanStokes How sweet of him! But this can't feed a template parameter on its own, can it? How can this help me performing the cast?Calica
It could profitably be used with enable_if. And underlying_type might help with your cast, although I confess I got a bit lost in your code by that stage.Shuffleboard
N
8

We can accomplish this with std::enable_if and std::is_enum. As a sample this is a class that will take an enum type as a template parameter.

#include <type_traits>
enum Enum { FOO, BAR};

template<typename T, typename std::enable_if<std::is_enum<T>::value>::type* = nullptr>
class Test {};

int main()
{
    Test<Enum> a; // ok
    Test<double> b; // error
}
Naseby answered 31/8, 2015 at 17:58 Comment(5)
Arf! I can't get this working. Is #include <type_traits> supposed to be enough to use your code? gcc says error: no type named ‘type’ in ‘struct std::enable_if<false, void>.Calica
That is the error you should get. if enable_if is passed false then the type typedef does not exist and it creates a compiler error. There should be a note after the error like: note: while substituting prior template arguments into non-type template parameter ...Naseby
Wops! Of course. I just got confused because clang located the error on the template line. This still looks magic to me: Are you aware of a good C++ generic programming tuto roaming by? Something going further than template <typename T> and covering those new C++11 amazing stuffs?Calica
There are some good books here: #388742Naseby
Great! I played around a little bit with this and this'll work indeed! Traits are awesome. Cheers!Calica

© 2022 - 2024 — McMap. All rights reserved.