質問

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).

役に立ちましたか?

解決

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.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top