Search code examples
c++gcctype-traitsstatic-assertprotothread

how to check std::is_base_of<> on (*this)


For certain task, I didn't manage to get away without macro. Now I'd like to add at least some protection from misuse.

I'd like to static_assert that MYMACRO() is used

  1. in a subclass of Base class,...
  2. ...namely, in run() method

A naive approach fails:

static_assert(std::is_base_of<Base, typeid(*this)>::value, "Use MYMACRO() only in subclass of Base.");
//                                  =============
//       SubOne would work, but not typeid(*this)
//
static_assert(__func__ == "run", "Use MYMACRO() only in run() method.");
//            ========
//       not a constexpr?
//

To reproduce:

#ifndef __GNUG__
#error "Needs GCC C++"
#endif

#define MYMACRO() \
{\
    do { \
    /*> > > want static_assert'ions here < < <*/\
    /*here comes stuff I coudn't put into an [inline] function,*/ \
    /*because it contains GCC Labels-as-Values and */ \
    /*conditional return;*/ \
    } while(false);\
}

class Base {
public:
    virtual int run() = 0;
};

class SubOne : Base {
public:
    int run() override {
        // ...
        MYMACRO();
        // ...
    };
};

class SubTwo : Base {
public:
    int run() override {
        // ...
        MYMACRO();
        // ...
    };
};

int main(void) 
{
    SubOne sub1;
    SubTwo sub2;
    //a little embedded app
    while (true) {
        sub1.run();
        sub2.run();
    }
}

Anticipating possible questions:
What is it for? - http://dunkels.com/adam/dunkels06protothreads.pdf
Labels as values: - https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
Why not "proper" RTOS with context switching? - I expect the above solution to simplify unit testing under native architecture, side-stepping the need for native (POSIX) port or QEMU/renode or target board. (Not for all, but for many tests)


Solution

  • typeid is the wrong tool here, because it returns a referene to a type_info instance, which is not the type of *this but only contains information on the type.

    You can use decltype:

    #include <iostream>
    #include <type_traits>
    
    
    struct base {};
    
    struct foo : base {
        foo() {
            static_assert(std::is_base_of<base,std::remove_reference<decltype(*this)>::type>::value);
        }
    };
    
    struct foo_fail {
        foo_fail() {
            static_assert(std::is_base_of<base,std::remove_reference<decltype(*this)>::type>::value);
        }
    };
    

    Compiler output:

    prog.cc: In constructor 'foo_fail::foo_fail()':
    prog.cc:15:23: error: static assertion failed
             static_assert(std::is_base_of<base,std::remove_reference<decltype(*this)>::type>::value);
                           ^~~