Tuesday, July 1, 2014

Determining whether if a function (or variable, or constant) is declared in C++11

As @mattn_jp pointed out on his blog entry, C/C++ allow use of redundant parenthesis in variable declaration. For example, both of the following statements conform to the language specification.

int i = 3; // declares variable i, initialized to 3
int (j) = 4; // declares variable j, initialized to 4

By using this feature, it is possible to create a macro that tests the existence of whatever variable or constant.

The macro: is_defined in the following example is one such implementation.

Compiling and running the code would likely emit is NAN defined? yes. But if you remove #include <cmath> the output will likely change to false, since NAN is a constant defined in cmath.

#include <iostream>
#include <cmath>

struct is_defined_t {
  static bool defined_;
  is_defined_t() { defined_ = false; }
  template <typename T> explicit is_defined_t(const T&) { defined_ = true; }

bool is_defined_t::defined_;

#define is_defined(arg) (([]() { is_defined_t(arg); return is_defined_t::defined_; })())

int main(int argc, char **argv)
  std::cout << "is NAN defined? " << (is_defined(NAN) ? "yes" : "no") << std::endl;
  return 0;

Shortcoming of the example is that the result can be only obtained at runtime. It would be useful if we could get the result at compile time, since once we have such thing, we can get rid of autoconf, CMake, etc.


  1. I don't agree this can replace autoconf, CMake or similar things. One of the significant goals for such configuration tools is to remove those compile time or runtime checking logic from the source code. With configuration tools, necessary checks are done during configuration time instead of compile time or runtime. As a result, we can keep the program source code simple.

    This opinion is not for denying usefulness of your work. It's surely useful to check mistakes in source code. I'd like to say it's a different thing. Comparing it with autoconf or CMake is just like comparing apples and oranges.

  2. Thank you for your comment. I agree that there would be a use case for autoconf or CMake even if we were possible to write this kind of expression that runs at compile-time.

    OTOH, I disagree with your statement that using autoconf or CMake simplifies the code (in the context of checking the existence of predefined functions etc.). When using such tools, you need use a preprocessor macro within the source code to change the behavior. So the complexity of the source code would be indifferent from the proposed hypothetical approach; it is merely a comparison of whether using preprocessor macro or C++ templates (to change behavior at compile-time).

    1. Oku-san, thank you for your reply.

      Well, after reading your comment, my opinion is still that autoconf, CMake or such configuration tools can simplify source code.

      Actually, preprocessor macros are not necessary if only existence of something is mandatory, because the source code does not have to be conditional. Since configuration fails if something mandatory is missing, so a user, who compiles and install the program, will not be able to compile the program when configuration fails.

      Preprocessor macros, often referred as a root of complexity, are required when developers want to make their program conditional. In other words, preprocessor macros are required if a developer wants to allow a user to compile a program even if the user's system doesn't have some functionality which is mandatory in the program or allow a user to compile a program without some additonal features. Such conditions are often checked during configuration or specified using --with or --enable option in configure script on autoconf or -D option on CMake.

      If a developer considers some functionality is mandatory for the program, he/she/xe can simply let configuration to fail if the user's system doesn't have it. In this case, the program does not have to contain a preprosessor macro for conditional use; I assume the program is kept simple and clean in this scenario. BTW, the actual problem is that developers want to make their program conditional too often, the problem doesn't exist in configuration tools themselves in this case IMHO.

    2. Thank you for the response.

      I disagree completely.

      It is true that there would be no complexity in the code if the intent of using autoconf is to assert that some mandatory function is missing on the target system. However in such case, there would be no complexity even if you did not use autoconf at all, since compiling code that calls the missing function will generate a compile error. In other words, there is no benefit in using autoconf in such case in terms of complexity of code.

      That said, it is true that autoconf or CMake would generate a more user-friendly error message. But that comes with the burden of using those external tools; it is a trade off issue.

    3. Hmm. Okay. I agree that autoconf or CMake doesn't ease complexity if a developer except the error is detected upon compile time. As you said, it's a matter of user-friendliness; it's better for users to let the program report any errors as early as possible. In this area, the is_defined() cannot replace autoconf or CMake.

      BTW, I'm now a little bit confused. What is a real benefit to use the is_defined() over AC_CHECK_DECLS etc? One benefit of autoconf or CMake is to move a logic to check existence of various things to macros from the program source code itself. In this sence, is_defined() introduces a certain complexity into the program source code, thus more complex than using autoconf/CMake.

    4. You need to do the following to write conditional code using autoconf:

      1. call AC_CHECK_FUNC in configure.in
      2. within the source code, use `#if` to test the macro set in step 1

      Under the hypothetical approach discussed on this blog post, you need to:

      1. within the source code, write a conditional statement that tests the existence of the function

      So the benefit of using the proposed approach would be: i) the logic does not get split into configure.in and the source code, ii) no use of “global variable” (a preprocessor definition in case of autoconf), iii) no dependency on external configuration tool.

  3. I'm puzzled with lots of exercises. I was afraid I could not do the right time despite my hard work. I need a support person. catmario4.com


Note: Only a member of this blog may post a comment.