41

I unfortunately have several macros left over from the original version of my library that employed some pretty crazy C. In particular, I have a series of macros that expect certain types to be passed to them. Is it possible to do something along the lines of:

static_assert(decltype(retval) == bool); 

And how? Are there any clever alternatives?

Yes I'm aware macros are bad. I'm aware C++ is not C, etc.

Update0

Here is some related code, and the source file. Suggestions are welcome. The original question remains the same.

3
  • Having seen the actual macros, I would strongly suggest to read Modern C++ Design or [More] Exceptional C++. Commented Oct 28, 2010 at 11:39
  • @MSalters: I can't imagine how C++ can provide implicit alternatives to my entry/exit, but I'll take a look. Also note the writing a FS is very unmeta, my goal is not to create some grand C++ framework. Commented Oct 31, 2010 at 2:30
  • You put an object on the stack on entry; any exit from the scope will destroy the object. Thus, on entry the ctor is called, and on exit the dtor is called. The automatic dtor call is especially useful, we know from history that typical programmers will miss the macro on ~5% of all exits from a function. Commented Nov 2, 2010 at 9:21

5 Answers 5

76

I found this to be the cleanest, using @UncleBens suggestion:

#include <type_traits> static_assert(std::is_same<decltype(retval), bool>::value, "retval must be bool"); 
Sign up to request clarification or add additional context in comments.

12 Comments

What is the c++03 equivalent?
@balki: Just upgrade to C++11. C++ is already bad enough, don't make it worse on yourself :)
When using std::is_same, be sure to #include <type_traits>.
what about const / reference qualifiers?
@einpoklum-reinstateMonica You can use std::remove_const, std::remove_reference etc. C++20 also has std::remove_cvref.
|
4

If you DO care about the const and volatile qualifiers, and want to ensure the const and volatile parts of the types also exactly match the type you are comparing against, do like @Matt Joiner says:

#include <type_traits> static_assert(std::is_same<decltype(my_variable), uint64_t>::value, "type must be `uint64_t`"); 

I you do NOT care about const, however, and want to simply ensure the type is a certain type without regard for const, do the following instead. Note that std::remove_const<>::type is required here:

static_assert(std::is_same<std::remove_const<decltype(my_variable)>::type, uint64_t>::value, "type must be `uint64_t` OR `const uint64_t`"); 

The same goes for volatile. In case you don't care about the volatile part of the type either, you can ignore it with std::remove_volatile<>::type:

static_assert(std::is_same<std::remove_volatile<decltype(my_variable)>::type, uint64_t>::value, "type must be `uint64_t` OR `volatile uint64_t`"); 

If you don't care about const OR volatile, you can remove them both with std::remove_cv<>::type:

static_assert(std::is_same<std::remove_cv<decltype(my_variable)>::type, uint64_t>::value, "type must be `uint64_t` OR `const uint64_t` OR `volatile uint64_t` OR `volatile const uint64_t`"); 

Note also that as of C++17 you can do:

  1. std::remove_cv_t<decltype(my_variable)> in place of std::remove_cv<decltype(my_variable)>::type, and:
  2. std::is_same_v<some_type, another_type> in place of std::is_same<some_type, another_type>::value.

References:

  1. Use static_assert to check types passed to macro
  2. https://en.cppreference.com/w/cpp/types/is_same
  3. https://en.cppreference.com/w/cpp/language/decltype
  4. https://en.cppreference.com/w/cpp/header/type_traits
  5. https://en.cppreference.com/w/cpp/types/remove_cv - std::remove_cv<>, std::remove_const<>, std::remove_volatile<>

Related:

  1. [another answer of mine where I use the above static_assert tricks] How to make span of spans
  2. Static assert in C [my own answer]
  3. How to use static assert in C to check the types of parameters passed to a macro [my own question]
  4. Typechecking macro arguments in C
  5. *****C++ Limit template type to numbers

Comments

3

Disclaimer: This is a bad answer, there are definitely far better solutions. Just an example :)

It is bound to be already implemented, but it's trivial to implement yourself;

template <class T1, class T2> struct CheckSameType; //no definition template <class T> struct CheckSameType<T,T>{}; // template <class T1, class T2> AssertHasType(T2) { CheckSameType<T1, T2> tmp; //will result in error if T1 is not T2 } 

To be used like this:

AssertHasType<bool>(retval); 

Alternative (suggested by GMan):

template <class T1, class T2> struct SameType { enum{value = false}; } template <class T> struct SameType<T,T> { enum{value = true}; }; 

To be used like

static_assert(SameType<decltype(retval), bool>::value); 

2 Comments

Not a bad answer/concept at all, though I don't understand AssertHasType and the following lines, syntactically. I'd recommend making meta-functions and a static assert utility, though, which provides two re-usable utilities that can combine to make the intended effect, rather than a custom check per condition.
Or use is_same from the type_traits header (I hope it is available with VC10).
3

It appears you need decltype because you've got an expression, but want to verify a type. There are already enough ways to do that now (C++03). For instance, to check a bool

inline void mustBeBool(bool) { } template<typename T> inline void mustBeBool(T t) { & (&t); } // Takes address of rvalue (&t) // Use: #define DifficultMacro(B) do { mustBeBool(B); foo(B); } while (false) 

1 Comment

For boost-level robustness, you'll need an address_of template to catch the idiots who have overloaded unary T::operator&()
1

Most macros can be replaced with inline functions and/or templates. As a case in point, the overly clever argument-size-checking Posix isnan macro is a template in C++0x. Oh,bad example, but you get the idea.

The main exceptions to that rule are macros that essentially implement higher level language features. For example, smarter exception handling, or covariance, or a parameterized set of declarations.

In some cases the macros that can't be reasonable expressed as inline functions or templates, can be replaced with a smarter kind of preprocessing, namely code generation. Then you have a script somewhere that generates the necessary code. For example, it's possible to do options classes in pure C++ with macros and templates, but it's hairy, and as an easier-to-grok and perhaps more maintainable alternative one might use a script that generates the requisite classes, at the cost of extra build steps and dealing with multiple languages.

Cheers & hth.,

1 Comment

+1. Having seen the code, it becomes obvious that the actual macro's are trivially replaced by a stack-allocated object, dramatically increasing reliability and readability. These macro's are used for function call/return logging.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.