Ajay
Ajay

Reputation: 18431

Static Assert to check compile-time-constant is NOT passed to macro

Preamble:

switch(nValue)
{
case X:
...
case Y:
...
default:
   ASSERT_FOR_DEFAULT(nValue);
}

ASSERT_FOR_DEFAULT is a macro, that will display a (custom) assertion dialog, to report hitting "default-case". Yes, this macro is for run-time assertion, not for compile time assertion. But, I just need that, any constant-value (compile-time) cannot be passed to this macro.

Problem:

Following should fail at compilation:

ASSERT_FOR_DEFAULT(5);

Yes, programmer may use it anywhere, not just in default case of a switch-case. He may also use any expression which is not used in switch. But that's not the issue. Just need that only non-constant should be passed to this macro.

There is nothing important written for this macro, just assume it is ASSERT/assert.

I have tried with templates (used them other SFINAE/static-asserts!), arrays (like strcpy_s), own struct having YES, NO types and what not. But couldn't find the solution!

I am using VC2008. I am aware of static_assert, decltype etc, but cannot use C++0x.


EDIT (Solution):

#define STATIC_ASSERT(expr) {int array[!!(expr)]; expr;}

template <class T>  
bool noConstAllowed(T&);

int noConstAllowed(...);

#define ASSERT_FOR_DEFAULT_VALUE(val)                \
{                                                  \
    STATIC_ASSERT(sizeof(noConstAllowed(val))==sizeof(bool));   \
}

int main()
{
    int test=10;

    ASSERT_FOR_DEFAULT_VALUE(test);
    ASSERT_FOR_DEFAULT_VALUE(2);
    ASSERT_FOR_DEFAULT_VALUE(test+2);  //FAILS, but okay for me! 
}

Thanks to Arne Mertz for this lovely suggestion. I derived the solution from it. noConstAllowed is overloaded for all T& types, and if constant value is passed, the another overload will be called. Both have different return types, and thus checking against the size. Templated version returns a bool, which satisfies the assert for any variable passed, and fails for any constant, or expression (since return type would be int).

Upvotes: 3

Views: 1730

Answers (1)

Arne Mertz
Arne Mertz

Reputation: 24626

You could redefine the macro so that it takes the address of the parameter - that should fail for literals. However that will not prevent you from passing a constant variable, e.g.

const static int FIVE = 5;
ASSERT_FOR_DEFAULT(FIVE); // still works.

To prohibit all kinds of constants from being passed to your macro call a function that takes the parameter by non-const reference:

template <class T>  void noConstAllowed(T&){};
#define ASSERT_FOR_DEFAULT_VALUE(val)                \
  {                                                  \
    (void*)&(val);         /* no literals*/          \
    noConstAllowed(val);   /* no constants at all */ \
    switchHitDefaultDialog(val, __FILE__, __LINE__); \
  }

I suppose you use something like FILE or other positional macros or you would have made it a function. Of yourse, you only need to use one of the two lines.

Upvotes: 3

Related Questions