开发者

How to print an integral template argument at compile time in C++

Say I have implemented a template class like this:

template <size_t N> 开发者_如何学Pythonclass C 
{
     void f()
     {
        // print out N here?
     }
};

I wish that while the compiler compiles a clause like

C<20> c;

it would print out a message

"class C is templated with N = 20"

I've tried with #pragma and static_assert in vain.

The problem is that

  1. with #pragma and static_assert, I could not embed an integral(20 here) into a message;
  2. with preprocessors, it's too early that N is not substituted with 20 yet.

Is there any way or no way?

Thanks.


You could add a post-build step that finds all instantiations within the output binary after all compilations of the template(s) are complete. For instance, using the GNU toolchain you could do this:

make
nm foo | c++filt | grep 'C<[^>]\+>::f'

Where foo is the name of the output binary.

The regular expression obviously needs to be altered to find the template instantiations you are looking for, but this example works for your example class C.

You could even use a very broad regex to find all template instantiations, of any kind:

grep '<[^>]\+>::'

This is incidentally a great way to illustrate how use of the STL or iostream libraries bloats even seemingly tiny programs. The number of template instantiations can be truly astounding!


In general, generating a warning seems to be a good approach (warning instead of error so you can log multiple things in one compilation run). Here's a function I've been using, which allows passing in a value and get its type printed, or pass a type as a template parameter:

template <typename T>                
inline void debug_type(const T&) __attribute__((deprecated));        

template <typename T>                                          
inline void debug_type(const T&) { }                           

template <typename T>                                        
inline void debug_type() __attribute__((deprecated));        

template <typename T>                                            
inline void debug_type() { } 

You can use it like this:

debug_type(1); // Pass a value, let the compiler deduce its type
debug_type<char>(); // Pass a type explicitly

This results in warnings like this:

foo.cpp:73:17: warning: 'void debug_type(const T&) [with T = int]' is deprecated (declared at /tmp/arduino_build_static/sketch/Num.h:13) [-Wdeprecated-declarations]
     debug_type(1);
                 ^
foo.cpp:74:22: warning: 'void debug_type() [with T = char]' is deprecated (declared at /tmp/arduino_build_static/sketch/Num.h:19) [-Wdeprecated-declarations]
 debug_type<char>();

The T = int part of the error message shows the type (which can of course be more complicated templated types, this is just an example).

A particular advantage of this warning over the unused variable warning proposed elsewhere is that the error location is the place where debug_type is called, not its implementation, so the code snippet shown in the error shows the expression whose type is being printed (which can be convenient when you want to print a few different ones at once).


Since the pre-processor phase occurs before template instantiation when you compile, you can't have the compiler emit a custom message based on something that a template does using pre-processor directives. Moreover, C++ templates, while extremely powerful, don't have any capacity to emit custom messages at compile time.


I'd go with Dan's approach personally.

If that's not an option, then there's no standard approach, but extending on some of the other options here, it is possible to make the compiler generating warnings for you that will show you the value:

template <int N> class C
{
public:
  C ()
  {
    int d1;
    int d1 = d1;  // Using uninitialized variable - warning
  }
};

C<10> c;

Using g++ with the -Wuninitialized option, the above generates:

t.cc: In constructor 'C<N>::C() [with int N = 10]':
t.cc:7: warning: 'i' is used uninitialized in this function

You could put this into a MACRO enabled for debug builds.


Do you want an error, if N == 20?

If so, how about specialization?

template <> class C <20> 
{
     int class_C_is_templated_with_20[-1];
}


The following prints out:

Test.cpp: In instantiation of ‘C<20>’:
Test.cpp:14: instantiated from here
Test.cpp:9: error: no matching
function for call to
‘assertion_failed(mpl_::failed************
(C<20>::CLASS_C_TEMPLATED_WITH_I_EQUAL_TO_::************)(mpl_::int_<20>))’

This prints out a semi-legible message, but also halts compilation. I'm not sure if this is what you are looking for.

#include <boost/mpl/assert.hpp>
#include <boost/mpl/int.hpp>

template<int i_>
class C
{
public:

BOOST_MPL_ASSERT_MSG( false, CLASS_C_TEMPLATED_WITH_I_EQUAL_TO_, (boost::mpl::int_<i_>) );

};

int main() {
C<20>();
}


It might be possible to generate a warning (e.g declare an unused instance of a useless empty struct in the f method). However, that warning, which will hopefully also mention the value of N will be triggered (if at all) only when you instantiate the method. On the other hand, it might be conditionally compiled depending on a macro.

Perhaps it will also be possible to put something in the class declaration that invokes the warning when the class itself is instantiated (without, for example, changing the size of the instance). I haven't had any luck triggering a warning while evaluating a static_assert with GCC, though.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜