Search code examples
c++templatesenable-if

Enum class with enable_if methods


I'm playing around with C++ a bit and was trying out templates, enum classes, and such. In my dabbling I encountered an issue that I'm not sure of a good way to solve it. I have two different types of "memory": mem1, and mem2. Both of these have special policies on how to access them but the same methods: create, read, & write. Now, depending on the access type of the memory, I want the compiler to give me an error if, for example, the access type is read-only and its calling write(). Here's what I have so far:

enum class mem1_access_type : int { write = 0, read = 1, rw = 2 };
enum class mem2_access_type : int { write = 3, read = 4, rw = 5 };

struct mem1_access
{
    mem1_access() : m(mem1_access_type::rw), p(0);
    mem1_access(mem1_access_type _m) : m(_m), p(0);

    mem1_access_type getAccess(){ return m; };
    int getP(){ return p; };

    private:
        mem1_access_type m;
        int p;
};

struct mem2_access
{
    mem2_access() : m(mem2_access_type::rw), p(0);
    mem2_access(mem2_access_type _m) : m(_m), p(0);

    mem2_access_type getAccess(){ return m; };
    int getP(){ return p; };

    private:
        mem2_access_type m;
        int p;
};

template <typename Access>
struct base_policy
{
    Access a;
    base_policy(Access _a) : a(_a) {};

    void create();
    //HERE
    void write();
    //AND HERE
    void read();

};

struct mem1_policy : base_policy<mem1_access>
{
    mem1_policy(mem1_access _a) : base_policy<mem1_access>(_a) {};
};

struct mem2_policy : base_policy<mem2_access>
{
    mem2_policy(mem2_access _a) : base_policy<mem2_access>(_a) {};
};

I was considering using std::enable_if for the write and read methods that checks the access type of the provided access. But I just can't think of how to go about this. What can be used to only compile methods depending on the access_type provided.

EDIT:

Thank you dog jones for your answer! It does exactly what I wanted it to!


Solution

  • Specifically what issues are you encountering when you try to use std::enable_if?

    EDIT:

    In order to determine if the base_policy class should have create / write / read defined at compile time (using templates), the access type of mem1_access and mem2_access must be compile-time constants:

    template <mem1_access_type accessType> struct mem1_access
    {
        // These typedefs will help 'store' the read/write access information for later:
        typedef mem1_access_type AccessType;
        typedef std::integral_constant<mem1_access_type, accessType> AccessValue;
    
        mem1_access() : p(0) {}
    
        mem1_access_type getAccess(){ return m; };
        int getP(){ return p; };
    
            static const mem1_access_type m = accessType;
        private:
            int p;
    };
    
    template <mem2_access_type accessType> struct mem2_access
    {
        typedef mem2_access_type AccessType;
        typedef std::integral_constant<mem2_access_type, accessType> AccessValue;
        mem2_access() : p(0) {}
    
        mem2_access_type getAccess(){ return m; };
        int getP(){ return p; };
    
            static const mem2_access_type m = accessType;
        private:
            int p;
    };
    

    Note that you can no longer set the access type in the constructor. It's now a template parameter, a compile time constant. Are you willing to make this trade off?

    Later, in base_policy, you can specify default template parameters and then specialize those:

    // Be default, this class can write():
    template <typename Access, bool shouldWrite=Access::AccessType::write == Access::m || Access::AccessType::rw == Access::m>
    struct base_policy
    {
        Access a;
        base_policy(Access _a) : a(_a) {};
    
        void create();
        //HERE
        void  write();
       //AND HERE
        void read();
    };
    
    // This class can't write():
    template <typename Access>
    struct base_policy<Access, false>
    {
        Access a;
        base_policy(Access _a) : a(_a) {};
    
        void create();
        //HERE
        //AND HERE
        void read();
    };
    

    Or use std::enable_if, which I think ought to look something like this (in the body of base_policy):

    std::enable_if<Access::AccessType::write == Access::m || Access::AccessType::rw == Access::m, void>::type  write();