35

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.

Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
  • Having seen the actual macros, I would _strongly_ suggest to read Modern C++ Design or \[More\] Exceptional C++. – MSalters Oct 28 '10 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. – Matt Joiner Oct 31 '10 at 02: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. – MSalters Nov 02 '10 at 09:21

5 Answers5

69

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");
Błażej Czapp
  • 2,478
  • 2
  • 24
  • 18
Matt Joiner
  • 112,946
  • 110
  • 377
  • 526
  • 3
    What is the c++03 equivalent? – balki Oct 31 '12 at 09:20
  • 11
    @balki: Just upgrade to C++11. C++ is already bad enough, don't make it worse on yourself :) – Matt Joiner Nov 02 '12 at 13:27
  • 7
    When using `std::is_same`, be sure to `#include `. – Jim Garrison Mar 12 '14 at 23:08
  • This works well for `boolvariable`, but fails for `(boolvariable)` - i.e., parentheses around the variable name cause the variable to be considered a non-boolean type even though it is declared boolean... – dolphin Oct 06 '14 at 21:32
  • 1
    what about const / reference qualifiers? – einpoklum Dec 10 '15 at 09:26
  • C++17 allows `static_assert(std::is_same_v);` – Dino Nov 18 '19 at 13:08
  • 1
    @einpoklum-reinstateMonica You can use `std::remove_const`, `std::remove_reference` etc. C++20 also has `std::remove_cvref`. – Dino Nov 18 '19 at 13:14
  • @Dino: Yes, I know, I was giving Matt a hint to incorporate that into his answer... – einpoklum Nov 18 '19 at 23:35
  • Documentation: https://en.cppreference.com/w/cpp/types/is_same and https://en.cppreference.com/w/cpp/language/decltype – Gabriel Staples Feb 28 '20 at 05:20
  • Is there any equivalent to this in C, or an alternative work-around using gcc extensions in C to achieve the same result? – Gabriel Staples Mar 10 '20 at 04:26
  • Question posted: ["How to use static assert in C to check the types of parameters passed to a macro"](https://stackoverflow.com/questions/60611626/how-to-use-static-assert-in-c-to-check-the-types-of-parameters-passed-to-a-macro) – Gabriel Staples Mar 10 '20 at 04:39
  • Great answer, @MattJoiner, but it took me a while to figure out how to remove `const` and `volatile` qualifiers, so I've expounded upon your answer in my answer here: https://stackoverflow.com/questions/4021981/use-static-assert-to-check-types-passed-to-macro/60769143#60769143. – Gabriel Staples Mar 20 '20 at 06:02
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);
Bart van Ingen Schenau
  • 15,488
  • 4
  • 32
  • 41
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 2
    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. – GManNickG Oct 26 '10 at 08:51
  • 1
    Or use `is_same` from the `type_traits` header (I hope it is available with VC10). – UncleBens Oct 26 '10 at 15:50
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)
MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 3
    For boost-level robustness, you'll need an `address_of` template to catch the idiots who have overloaded unary `T::operator&()` – MSalters Oct 26 '10 at 14:04
3

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
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
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.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Please provide a reason when you downvote. That helps others understand what's wrong with an answer. Or alternatively, it can help others disregard the downvote. – Cheers and hth. - Alf Oct 27 '10 at 11:00
  • +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. – MSalters Oct 28 '10 at 11:38