Consider this example, from bug 80985:
template <class Func>
void call(Func f)
{
f();
}
void func() noexcept { }
int main()
{
call(func);
}
Compiling this with all warnings enabled, as you do, yields:
$ g++ -std=c++14 -Wall foo.cxx
foo.cxx:2:6: warning: mangled name for ‘void call(Func) [with Func = void (*)() noexcept]’ will change in C++17 because the exception specification is part of a function type [-Wnoexcept-type]
void call(Func f)
^~~~
What exactly am I supposed to do with this warning? What is the fix?
There's several things you can do about the warning message.
Disable it with -Wno-noexcept-type. In many projects the warning message is unhelpful because there's no chance the resulting object will be linked with an another object that expects it to use GCC's C++17 name mangling. If you're not compiling with different -std= settings and you're not building a static or shared library where the offending function is part of its public interface then the warning message can safely disabled.
Compile all your code with -std=c++17. The warning message will go away as the function will use the new mangled name.
Make the function static. Since the function can no longer be referenced by another object file using a different mangling for the function the warning message will not be displayed. The function definition will have to be included in all compilation units that use it, but for template functions like in your example this is common anyways. Also this won't work for member functions were static means something else.
When calling a function template specify the template parameter explicitly giving a compatible function pointer type that doesn't have the exception specification. For example call<void (*)()>(func). You should also be able to use cast to do this as well, but GCC 7.2.0 still generates a warning even though using -std=c++17 doesn't change the mangling.
When the function isn't a template don't use noexcept with any function pointer types used in the function's type. This and the last point rely on the fact that only non-throwing function pointer types result in naming mangling changes and that non-throwing function pointers can be assigned (C++11) or implicitly converted (C++17) to possibly throwing function pointers.
I'm upvoting Ross's answer for the call<void (*)()>(func) solution. It explicitly tells the compiler that you want the template instantiated for a non-noexcept function type, and guarantees that your code will operate exactly the same in C++17 as it did in C++14.
More alternatives are:
(1) Wrap the noexcept function in a lambda (which isn't noexcept):
template <class Func>
void call(Func f)
{
f();
}
void func() noexcept { }
int main()
{
call([]() { func(); });
}
(2) Create a separate wrapper function without noexcept. This is more typing initially, but if you have multiple call sites it could save typing overall. This is what I ended up doing in the code that originally prompted me to file the GCC bug.
In addition to what is already said, I have found another way to get rid of this warning in GCC 7. Apparently, GCC generates this warning if and only if the first instantiation of call() involves noexcept. So a solution would be first to instantiate call() with a not noexcept function.
This trick would also do:
using dummy = decltype(call(std::declval<void(*)()>()));
P.S. GCC 8.2.1 does not report a warning in this case.
The issue they are warning you about is that in C++14, this will work:
void call(void (*f)())
{
f();
}
void func() noexcept {}
int main(int argc, char* argv[])
{
call(&func);
return 0;
}
but in C++17, you would need to change the declaration of call to be:
void call(void (*f)() noexcept)
{
f();
}
Since you have defined call to be a template, you don't need to worry about this. Still, it could cause you problems because the inferred type is changing, which doesn't usually happen.
For example, this code will compile in C++14 but not C++17:
void foo() noexcept {}
void bar() {}
template <typename F>
void call(bool b, F f1, F f2)
{
if (b)
f1();
else
f2();
}
void foobar(bool b)
{
call(b, &foo, &bar);
}
In C++14, the types of foo and bar are the same, but they are different in C++17, meaning the template resolution will fail. The error message in gcc 7.2 with the flag -std=c++1z is:
note: template argument deduction/substitution failed:
note: deduced conflicting types for parameter 'F' ('void (*)() noexcept' and 'void (*)()')
In the example you've given, there is no issue and you won't have a problem compiling in C++14 or C++17 mode. If the code is more complex than the example here (e.g. similar to the examples I gave above), you could encounter some compiler problems. It seems you have a recent compiler; try compiling with -std=c++1z and see if there are warnings or errors.
Related
The following compiles without error in VS2019 (version 16.11.15) with C++ 17 selected as the language. But it fails with C++ 20 with error "error C2027: use of undefined type 'Anon'"
template <typename T> class a_template
{
public:
void do_something(class Anon& anon) const;
};
template <typename T> void a_template<T>::do_something(class Anon& anon) const
{
anon.do_something();
}
The Anon class is of course undefined but the ::do_something function is unused so does not need to be instantiated. This is OK in C++17 but apparently not in C++20.
Is this a change in language rules? If so, can it be fixed without actually defining Anon?
Is this a change in language rules?
No, this is due to the fact that a C++ compiler is permitted (but not required!) to diagnose errors at the time the template is parsed
when all of the instantiations of the template would produce that error.
This means that for your given example, at the time of parsing the definition compilers may or may not issue an error. That is, the compiler can produce an error when parsing the template or may wait until the first template instantiation. Refer to demo where msvc doesn't issue an error but gcc and clang does.
Perhaps a simpler example would make it more clear :
void func()
{
}
template<typename T> void bar()
{
func(3); //compilers are allowed(but not required) to issue error at the time of pasrsing this
}
In the above example, func is a nondependent name and at the point where we have called func using func(3), the only visible func is the one that accepts 0 arguments and not one. And as i said earlier, some compiler may issue an error(at the time of parsing) even though we've not instantiated bar but some compilers may not. This is because they are allowed to but not required to do so. See demo where msvc doesn't issue an error here but gcc and clang does.
The same logic applies to your example as well. Meaning as Anon is an incomplete type and you have anon.do_something(), some compiler might choose to produce an error even though you have not instantiated a_template and some other compiler might not.
Supposedly std::abs is not constexpr in the standard (even in C++20). But in practice I found out that I can compile it as constexpr under the very peculiar condition that the function is templated. See this completely working example:
template<class T>
constexpr T f(const T input) {
return std::abs(input);
}
int main() {
int i = -1;
int a = f(i);
return 0;
}
The code:
Compiles fine with GCC, with and without the template.
It doesn't work in Clang.
And in Visual Studio it compiles with the template line, but fails compilation without the template.
For a regular function the compiler may know, based on the type of the function parameters, if the inner code can be potentially evaluated in compile time. This is why you get an error for calling std::abs in MSVC and clang. The behavior of gcc is based on its decision to implement std::abs as constexpr which is by the way a questionable decision.
For a template function the compiler cannot know if the inner code can be evaluated in compile time, as it may be based on the actual type of the template arguments, with different functions overload being called. While most compilers would decide not to check whether all possible overloads of std::abs cannot be constexpr, thus letting the code pass compilation, theoretically a compiler may check (in very specific cases that can be checked, like this one) and since the user is not allowed to extend std by adding a new version of abs (the list of allowed extensions to std is closed by the spec) it is possible to see that the function can never be constexpr and thus to generate a compilation error. In the more general case however, the compiler cannot check for a template function if all possible cases cannot produce a constexpr function, since it sees only the available overloads for the inner call, per each call to the template function, and there might be other available overloads for the inner call, when the template is called elsewhere.
Note that making a constexpr function a template, just so it can get compiled, would not be a good approach. The actual decision if the function is constexpr (i.e. can be called in compile time) would be based on the actual call, and if in all cases the function cannot be constexpr you are trying in a way to cheat the compiler but eventually are cheating mainly yourself...
By the way, in my check with clang 10.1 and trunk versions, I don't get compilation error on the template version, this code compiles both with gcc and clang:
template<typename T>
constexpr T myabs(T t) {
return std::abs(t);
}
int main() {
int i = myabs(3);
}
While this compiles with gcc (which implements std::abs as constexpr) and fails with clang:
int main() {
constexpr int i = myabs(3);
}
It seems that both gcc and clang do not generate an error even if the inner call inside a constexpr template function is not dependent on the template parameters and can never be a constant expression:
int myabs() {
return 42;
}
template<class T>
constexpr int f() {
// this is never a contexpr
// yet gcc and clang are ok with it
return myabs();
}
And again, this is allowed as no diagnostic is required for non-conforming constexpr template functions:
[dcl.constexpr] 9.2.5/7 - The constexpr and consteval specifiers:
[...] If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required.
In this question:
Print template typename at compile time
we have a few suggestions regarding how to get typical C++ compilers to print a type's name, at compile time. However, they rely on triggering a compilation error.
My question: Can I get the C++ compiler to print the name of a type without stopping compilation?
In general the answer is "probably not", because a valid program can be compiled into its target object without printing anything anywhere, so I'm asking specifically about GCC and clang, with possible use of preprocessor directives, compiler builtins, or any compiler-specific trick.
Notes:
Obviously, the challenge is printing types behind using/typedef statements, template parameter values, variadic templates etc. If the type is available explicitly you could just use something like #message "my type is unsigned long long" (as #NutCracker suggested). But that's not what the question is about.
Answers relying on C++11 or earlier are preferred to requiring C++14/17/20.
gcc and clang offers some interface for using own plugins which can do nearly everything on different stages from parsing to code generation.
The interfaces are compiler specific and as this a plugin for gcc can not be used for clang or visa versa.
The documentation is havy and there is no chance to go in any detail here, so I only point you to the docs from gcc and clang:
gcc plugin
clang plugin
The following mechanism is due to #JonathanWakely, and is specific to GCC:
int i;
template <typename T>
[[gnu::warning("your type here")]]
bool print_type() { return true; }
bool b = print_type<decltype(i)>();
This gives you:
<source>:In function 'void __static_initialization_and_destruction_0(int, int)':
<source>:7:33: warning: call to 'print_type<int>' declared with attribute warning: your
type here [-Wattribute-warning]
7 | bool b = print_type<decltype(i)>();
| ~~~~~~~~~~~~~~~~~~~~~~~^~
See it working on Godbolt.
In c++17 we can abuse the [[deprecated]] attribute to force the compiler to issue a warning containing the desired template parameter:
template<typename T>
[[deprecated]] inline constexpr void print_type(T&& t, const char* msg=nullptr){}
print_type(999, "I just want to know the type here...");
The snippet above will print the following warning with gcc:
<source>:32:59: warning: 'constexpr void print_type(T&&, const char*) [with T = int]' is deprecated [-Wdeprecated-declarations]
print_type(999, "I just want to know the type here...");
In contrast to the accepted answer this will work with every c++17 compliant compiler. NB that you will have to enable \W3` on MSVC.
We can even go further and define a static assert macro that will print the type if and only if it fails.
template<bool b, typename T>
inline constexpr bool print_type_if_false(T&& t) {
if constexpr (!b)
print_type(std::forward<T>(t));
return b;
}
// Some nice static assert that will print the type if it fails.
#define STATIC_ASSERT(x,condition, msg) static_assert(print_type_if_false<condition>(x), msg);
Here is a live example.
If a normal function calls a function that has not been declared yet, I get a compile-time error:
void foo(int x)
{
bar(x); // ERROR: bar has not been declared yet
}
void bar(int x)
{
std::cout << x << '\n';
}
int main()
{
foo(42);
}
The fix is to either forward-declare the called function, or to switch the order of definitions.
However, these fixes do not seem to be necessary with function templates:
template<typename T>
void foo(T x)
{
bar(x); // OKAY
}
template<typename T>
void bar(T x)
{
std::cout << x << '\n';
}
int main()
{
foo(42);
}
This compiles just fine. Why is that? When the compiler sees bar(x), why does it not complain?
(I am using g++ 4.6.3)
This is a "why is the sky made out of bricks" type question. Ie, a question that asks why something false is true. It is not the case that in C++ your code is legal.
Live example, as you can see in gcc 4.8 this does not actually compile.
I guess the question "why does gcc 4.6 let this code compile" remains. One of the things that compilers did early on when writing template expanders was to treat them as something similar to macros. Very little would be done when they where declared, and everything would be looked up when they where instantiated.
Compilers now tend to do more thing when the template is declared, and less when it is instantiated. This is what the C++ standard requires, or is at least closer.
As it happens, ADL can get around this: bar lookups that find bar via ADL do not have to be visible at the point where foo is written, but rather at the point of instantiation.
The gcc 4.8 error message is pretty self explanatory:
prog.cpp: In instantiation of ‘void foo(T) [with T = int]’:
prog.cpp:16:7: required from here
prog.cpp:6:10: error: ‘bar’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
bar(x); // OKAY
^
prog.cpp:10:6: note: ‘template<class T> void bar(T)’ declared here, later in the translation unit
void bar(T x)
^
these requirements may have been changed or clarified in C++11, so it is possible that gcc 4.6's behavior was legal under the C++03 standard.
When the compiler first sees bar(x), it doesn't know x's type, hence it can't look up the correct bar. Only when you instantiate foo, T and therefore x's type are known and bar(x) can be looked up.
Note that this work only for dependent expression, i.e. expressions that depend on a template parameter. If you add bar(42), it will fail to compile even if it is later instantiated with T==int.
You might also want to google "two-phase lookup" for further information. Only recent versions of GCC implement those rules correctly, as some checks also need to be done during the first phase of parsing the template. As pointer out by Yakk, newer versions of GCC reject your code, so always check with up-to-date versions of GCC or Clang to be on the safe side.
A function template isn't a function; it's a recipe for making functions, once the template parameters are known.
The compiler can't look up what bar means when it sees the foo template definition, because what it means could depend on what T is. So it just remembers that there's a use of the name bar that will need to be worked out later.
When you call foo(42) the compile has to produce (instantiate) the real function, and at that point it looks up the names it wasn't able to before, finds your bar template (and triggers instantiation of that too) and all is well.
For a normal function all names can be looked up when the function is defined, and so they must be properly declared at that point.
Say I have implemented a template class like this:
template <size_t N> class 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
with #pragma and static_assert, I could not embed an integral(20 here) into a message;
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.