C++ constexpr function to test preprocessor macros
autoconf, c++, c++11, c-preprocessor, constexpr
Solution
autoheader creates #undefs for all macros, even the ones which eventually get commented out. So I could write a script which scans config.h and generates a constexpr function for each macro. The question is where it should be inserted in the build process and how it's called.
If you are satisfied (as I think your should be) that you can't accomplish the goal purely in C++ and will need some custom build support as per quote, then perhaps you are seeing one complication that isn't there.
I don't see that you need a function, either one per `HAVE_XXXX`-macro or one for all `HAVE_XXXX`-macros, to tell you whether a given macro is defined. I mean I see no reason why you should start by pondering how this:
#ifdef HAVE_XXXX
#define MY_HAVE_XXXX true
#else
#define MY_HAVE_XXXX false
#endif
constexpr bool have_xxxx ()
{
return MY_HAVE_XXXX;
}
can be automated for all `HAVE_XXXX`-macros. You could replace that either in a `.h` file or a `.cpp` with:
#ifdef HAVE_XXXX
bool const have_XXXX = true;
#else
bool const have_XXXX = false;
#endif
In C++, `const` objects have internal linkage by default, so even in a header file these definitions run no risk of multiple definition errors at link time.
In that light, a customized build solution would execute a script to parse the `config.h` and write out a header file, say `config_aux.h` that includes `config.h` and contains:
#ifdef HAVE_XXXX
bool const have_xxxx = true;
#else
bool const have_xxxx = false;
#endif
for each `HAVE_XXXX`.
You ensure that `config_aux.h` is built as a prerequisite of everything else and then for all your compiles you ensure that `config_aux.h` is pre-included by the compiler in every translation unit. The way to do that is to pass g++ the option:
-include config_aux.h
See this answer
On the other hand, I think you're on the wrong track as to how you would actually use `have_xxxx` to do what you want.
In a comment you have linked to this answer, indicating that you see that `enable-if`-SFINAE technique as a model for statically enabling or disabling a member function, by making it a template member function with two SFINAE alternatives: one that would be chosen by template resolution when `have_xxxx` is true, and one that will be chosen when `have_xxxx` is false.
That's not actually a model for your requirement. It shows how to SFINAE a member function between one implementation that does the business when applicable per template parameter and alternative implementation that is a no-op.
But you don't want your program to do nothing if a statically disabled member function gets called. You want such a call to cause a compilation error. E.g. you don't want a call to `T::cbegin()` to be a no-op when `HAVE_CXX11` is false: you want the call to be a compiletime error.
So you don't need SFINAE, but you do need the member function to become a template member function, because template resolution is the only mechanism for statically enabling or disabling it within the same class (not counting the old `#ifdef` way).
It might seem that the obvious solution is illustrated in the following program:
#include <type_traits>
#define HAVE_XXXX 1 // Pretend we get this from autoconf
// Pretend we get this from config_aux.h
#ifdef HAVE_XXXX
bool const have_xxxx = true;
#else
bool const have_xxxx = false;
#endif
struct X
{
void a(){}
template<typename R = typename std::enable_if<have_xxxx,void>::type>
R b() {}
};
int main()
{
X x;
// x.b();
return 0;
}
With `HAVE_XXXX` defined it compiles, implementing both `X::a` and `X::b`. It could make the commented-out call to `X::b`. But if `HAVE_XXXX` were not defined then any call to `X::b` would require instantation of that member function with `std::enable_if<false,void>` and that would not compile.
But just edit the program so that `HAVE_XXXX` is not defined and rebuild it.
The build fails:
error: ‘type’ in ‘struct std::enable_if’ does not name a type
even though the program still makes no calls to `X::b`. You can't build the program at all unless `HAVE_XXXX` is defined.
The problem here is that the compiler can always evaluate `have_xxxx` without resort to template resolution. So it does; so it always discovers that error.
To prevent this, you would need the condition of the `enable_if` to depend on a template parameter of the function, so it will only be evaluated in template resolution, but still always to be true [false], if it is evaluated, as long `have_cxxx` is true [false]. Anything to that effect will do and a condition like:
!std::is_same<R,R>::value || have_xxxx
might come to mind. But:
template<
typename R =
typename std::enable_if<(!std::is_same<R,R>::value || have_xxxx),void>::type
>
R b() {}
will not compile any which way, for the elementary reason that it attempts to use the template parameter `R` to define the default type of itself.
But as `std::enable_if` has this irrelevant snag, why resort to it at all? A `static_assert` of a suitable condition within the body of `X::b` will do just was well. Diagnostically, it will do better. So instead define `X::b` like:
template<typename R = void>
R b() {
static_assert(!std::is_same<R,R>::value || have_xxxx,
"X::b is unimplemented without XXXX support");
}
Now, the program will compile whether `HAVE_XXXX` is defined or not and when it is defined you can also uncomment the call to `X::b`. But if `HAVE_XXXX` is not defined, and you also uncomment the call to `X::b`, then the member function gets instantiated; R is defined; the condition of the `static_assert` is evaluated, found false, and the `static_assert` fires. This is the outcome you want.
Problem
In my C++ project (I use autotools) I have a class with `begin()` and `end()` member functions, and I want to optionally include `cbegin()` and `cend()`, if and only if C++11 is supported. I test C++11 support using an M4 file from the Autoconf Archive. It's a macro which defines HAVE_CXX11 if supported, otherwise it doesn't define it. The C way to do what I want is: ``` #ifdef HAVE_CXX11 const_iterator cbegin () const; const_iterator cend () const; #endif ``` But I want to do some things in the C++ way. In this case I can use `std::enable_if` to optionally allow `cbegin` and `cend`. Like this: ``` #ifdef HAVE_CXX11 #define MY_HAVE_CXX11 true #else #define MY_HAVE_CXX11 false constexpr bool have_cxx11 () { return MY_HAVE_CXX11; } /* Now use have_cxx11() with std::enable_if */ ``` This works fine with a single specific macro, but what if I want to automate it for any given macro? In other words, what I want is to get a boolean indicating whether a given macro is defined. I see two options: - Have a specific function for each macro - Have a single function Example for 1: When autoconf defines its variable HAVE_CXX11, it will also define have_cxx11() to return true or false depending on whether the HAVE_CXX11 autoconf variable (not the macro constant) is defined to 0 or 1. Example for 2: The can be a function `macro_is_defined()` which returns a boolean, e.g. `macro_is_defined(HAVE_CXX11)` will return `true` when I build my C++11 project. I tried to find a way to implement these ideas in pure C++, but found none. I had to make a single line of code expand into a block of preprocessor directives, which IIRC is impossible. What should I do? And is it a good idea to try doing things the C++ way like I try, or it's too much? EDIT: autoheader creates `#undef`s for all macros, even the ones which eventually get commented out. So I could write a script which scans config.h and generates a constexpr function for each macro. The question is where it should be inserted in the build process and how it's called.