How to See the Template Instantiated Code by C++ Compiler

Can we see the template instantiated code by C++ compiler

You can definitely see the assembly code generated by the g++ using the "-S" option.

I don't think it is possible to display the "C++" equivalent template code - but I would still want a g++ developer to chime in why - I don't know the architecture of gcc.

When using assembly, you can review the resulting code looking for what resembles your function. As a result of running gcc -S -O1 {yourcode.cpp}, I got this (AMD64, gcc 4.4.4)

_Z3addIiET_S0_S0_:
.LFB2:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
leal (%rsi,%rdi), %eax
ret
.cfi_endproc

Which really is just an int addition (leal).

Now, how to decode the c++ name mangler? there is a utility called c++filt, you paste the canonical (C-equivalent) name and you get the demangled c++ equivalent

qdot@nightfly /dev/shm $ c++filt 
_Z3addIiET_S0_S0_
int add<int>(int, int)

Template instantiation details of GCC and MS compilers


Point of instantiation

templates will be instantiated when actually used

Not exactly, but roughly. The precise point of instantiation is a bit subtle, and I delegate you over to the section named Point of instantiation in Vandevoorde's/Josuttis' fine book.

However, compilers do not necessarily implement the POIs correctly: Bug c++/41995: Incorrect point of instantiation for function template


Partial instantiation

templates will be instantiated when actually used

That is partially correct. It is true for function templates, but for class templates, only the member functions that are used are instantiated. The following is well-formed code:

#include <iostream>

template <typename> struct Foo {
void let_me_stay() {
this->is->valid->code. get->off->my->lawn;
}

void fun() { std::cout << "fun()" << std::endl; }
};

int main () {
Foo<void> foo;
foo.fun();
}

let_me_stay() is checked syntactically (and the syntax there is correct), but not semantically (i.e. it is not interpreted).


Two phase lookup

However, only dependent code is interpreted later; clearly, within Foo<>, this is dependent upon the exact template-id with which Foo<> is instantiated, so we postponed error-checking of Foo<>::let_me_alone() until instantiation time.

But if we do not use something that depends on the specific instantiation, the code must be good. Therefore, the following is not well-formed:

$ cat non-dependent.cc
template <typename> struct Foo {
void I_wont_compile() { Mine->is->valid->code. get->off->my->lawn; }
};
int main () {} // note: no single instantiation

Mine is a completely unknown symbol to the compiler, unlike this, for which the compiler could determine it's instance dependency.

The key-point here is that C++ uses a model of two-phase-lookup, where it does checking for non-dependent code in the first phase, and semantic checking for dependent code is done in phase two (and instantiation time) (this is also an often misunderstood or unknown concept, many C++ programmers assume that templates are not parsed at all until instantiation, but that's only myth coming from, ..., Microsoft C++).


Full instantiation of class templates

The definition of Foo<>::let_me_stay() worked because error checking was postponed to later, as for the this pointer, which is dependent. Except when you would have made use of

explicit instantiations

cat > foo.cc
#include <iostream>

template <typename> struct Foo {
void let_me_stay() { this->is->valid->code. get->off->my->lawn; }
void fun() { std::cout << "fun()" << std::endl; }
};

template struct Foo<void>;
int main () {
Foo<void> foo;
foo.fun();
}

g++ foo.cc
error: error: ‘struct Foo<void>’ has no member named ‘is’


Template definitions in different units of translation

When you explicitly instantiate, you instantiate explicitly. And make all symbols visible to the linker, which also means that the template definition may reside in different units of translation:

$ cat A.cc
template <typename> struct Foo {
void fun(); // Note: no definition
};
int main () {
Foo<void>().fun();
}

$ cat B.cc
#include <iostream>
template <typename> struct Foo {
void fun();

};
template <typename T>
void Foo<T>::fun() {
std::cout << "fun!" << std::endl;
} // Note: definition with extern linkage

template struct Foo<void>; // explicit instantiation upon void

$ g++ A.cc B.cc
$ ./a.out
fun!

However, you must explicitly instantiate for all template arguments to be used, otherwise

$ cat A.cc
template <typename> struct Foo {
void fun(); // Note: no definition
};
int main () {
Foo<float>().fun();
}
$ g++ A.cc B.cc
undefined reference to `Foo<float>::fun()'

Small note about two-phase lookup: Whether a compiler actually implements two-phase lookup is not dictated by the standard. To be conformant, however, it should work as if it did (just like addition or multiplication do not necessarily have to be performed using addition or multiplication CPU instructions.

Where are template functions instantiated?

In what object file is func<int> instantiated?

In every object file (aka translation unit) that invokes it or takes an address of it when the template definition is available.



Why will the One Definition Rule not be violated?

Because the standard says so in [basic.def.odr].13.

Also see https://en.cppreference.com/w/cpp/language/definition

There can be more than one definition in a program of each of the following: class type, enumeration type, inline function, inline variable (since C++17), templated entity (template or member of template, but not full template specialization), as long as all of the following is true...



For this basic application of templates, is there any benefit to explicitly instantiating func<int> separate from its use?

In this case you get no inlining but possibly smaller code. If you use link-time code generation, then inlining may still happen.

Is it possible to view C++ code with instantiated templates before it gets compiled (g++)?

Well, the closer you can get is to read the AST/ABT generated by the compiler:

  • AST: Abstract Syntax Tree
  • ABT: Abstract Binding Tree

The former represents the view of the syntax as the compiler understands it and the latter is similar after resolution of the bindings (ie, that the a here is actually the variable that was declared 3 lines before or that the foo correspdonds to the function defined in that header...).

Clang allows to dump its AST... which is in fact the ABT, actually, it's being improved at this very moment; sneak developer preview:

int Test __attribute__((visibility("default")));

int main(int argc, char** argv) {
int x __attribute__((aligned(4))) = 5;
int y = 2;
for (;;)
if (x != y++)
break;
return (x * y);
}

Sample Image

Normally you should see how the template was instantiated there.

Note: to get it you need the -ast-dump pass to the clang front-end.

How to instantiate, by the standard, a class template containing nested subclass templates

There's a few things going wrong here:

template <class C> struct MyData : Data<C> {
MyData::Item1<int> item10;
};

MyData is an incomplete type here, so we can't use it just yet in that way. However, since Item1 is inherited from Data<C>, we should be able to just refer to it directly:

Data<C>::Item1<int> item10;

But now, Data<C>::Item1 is a dependant type, so we need to use typename to tell the compiler that it is, in fact, a type:

typename Data<C>::Item1<int> item10;

Finally, because Item1 is a template member of a dependant type, we also need to tell the compiler that it is a template (as opposed to a constant or something).

typename Data<C>::Item1 template<int> item10;

So at the end, we land at:

template<class C> struct Data {
template<typename T> struct Item1 {
void Test() {
}
};
};

template <class C>
struct MyData : Data<C> {
typename Data<C>::template Item1<int> item10;
};

struct S;

MyData<S> dataS;

Detect the existence of a template instantiation for a given type

Here's a link-time solution. Works on GCC, Clang, and MSVC.

One template (impl::Checker<T>) declares a friend function and calls it.

Another template (impl::Marker) defines that function. If it's not defined, the first class gets an undefined reference.

run on gcc.godbolt.org

#include <cstddef>
#include <type_traits>

namespace impl
{
template <typename T>
struct Checker
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif
friend void adl_MarkerFunc(Checker<T>);
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif

static std::nullptr_t Check()
{
adl_MarkerFunc(Checker<T>{});
return nullptr;
}

inline static const std::nullptr_t check_var = Check();
static constexpr std::integral_constant<decltype(&check_var), &check_var> use_check_var{};
};

template <typename T>
struct Marker
{
friend void adl_MarkerFunc(Checker<T>) {}
};
}

template <typename T, impl::Checker<T> = impl::Checker<T>{}>
struct Access
{
template <typename U>
void Read()
{
static_assert(std::is_same_v<T, U>);
(void)impl::Marker<U>{};
}
};

int main()
{
Access<int> x;
x.Read<int>();

[[maybe_unused]] Access<float> y; // undefined reference to `impl::adl_MarkerFunc(impl::Checker<float>)'

using T [[maybe_unused]] = Access<double>; // undefined reference to `impl::adl_MarkerFunc(impl::Checker<double>)'
}

Had to introduce a dummy template parameter to Access, since I couldn't think of any other way of detecting it being used in a using.



Related Topics



Leave a reply



Submit