Vineel Kumar Reddy
Vineel Kumar Reddy

Reputation: 4726

Can we see the templates instantiated by the C++ compiler?

Is there a way to see the compiler-instantiated code for a function template or a class template in C++?

Assume I have the following piece of code:

template <class T> T add(T a, T b) {
    return a + b;
}

When I call:

add<int>(10, 2); 

... I would like to see the function that the compiler creates for the int template specialization.

I am using g++, VC++, and need to know the compiler options to achieve this.

Upvotes: 78

Views: 28210

Answers (8)

SergeyA
SergeyA

Reputation: 62583

Now there is an on-line tool which does this for you: https://cppinsights.io/ For example, this code

template<class X, class Y> auto add(X x, Y y) {
  return x + y;
}

int main()
{
  return add(10, 2.5);
}

Is translated to

template<class X, class Y> auto add(X x, Y y) {
  return x + y;
}

/* First instantiated from: insights.cpp:9 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
double add<int, double>(int x, double y)
{
  return static_cast<double>(x) + y;
}
#endif


int main()
{
  return static_cast<int>(add(10, 2.5));
}

https://cppinsights.io/s/0b914fd3

Upvotes: 29

Jimmie_Cricket
Jimmie_Cricket

Reputation: 21

I know this is old, however for modern g++ you can use:

g++ -fdump-tree-*switch* -o array_size array_size.cpp

for switch use all to see all the types that can be used, you'll get about 32 files some are really big. You can use the last component of the file name as the switch to get just that file such as -fdump-tree-gimple.

This code:

#include <iostream>

template <typename T, size_t N>
size_t ARRAYSIZET(T (&a)[N])
{
    return N;
}

int main (int argc, char * argv[])
{
    int a[] = {1,2,3,4,5,6};
    std::cout << ARRAYSIZET(a) << std::endl;
}

Turns into: g++ -fdump-tree-gimple -o array_size array_size.cpp

int main (int argc, char * * argv)
{
  int D.53868;

  {
    int a[6];
    try
      {
        a[0] = 1;
        a[1] = 2;
        a[2] = 3;
        a[3] = 4;
        a[4] = 5;
        a[5] = 6;
        _1 = std::basic_ostream<char>::operator<< (&cout, 6);
        std::basic_ostream<char>::operator<< (_1, endl);
        _2 = ARRAYSIZET<int, 6> (&a);
        _3 = std::basic_ostream<char>::operator<< (&cout, _2);
        std::basic_ostream<char>::operator<< (_3, endl);
      }
    finally
      {
        a = {CLOBBER(eol)};
      }
  }
  D.53868 = 0;
  return D.53868;
}

size_t ARRAYSIZET<int, 6> (int[6] & a)
{
  size_t D.53874;

  D.53874 = 6;
  return D.53874;
}

man g++ and search for "dump".

Upvotes: 2

random
random

Reputation: 4038

Clang (https://clang.llvm.org/) can pretty-print AST of instantiated template:

For your example:

test.cpp

template < class T> T add(T a, T b){
    return a+b;
}

void tmp() {
    add<int>(10,2); 
}

Command to pretty-print AST:

$ clang++ -Xclang -ast-print -fsyntax-only test.cpp

Clang-5.0/Clang 14.0 output:

template <class T> T add(T a, T b) {
    return a + b;
}
template<> int add<int>(int a, int b) {
    return a + b;
}
void tmp() {
    add<int>(10, 2);
}

Upvotes: 107

dan_waterworth
dan_waterworth

Reputation: 6441

If your looking for the equivalent C++ code then no. The compiler never generates it. It's much faster for the compiler to generate it's intermediate representation straight off than to generate c++ first.

Upvotes: 1

mtvec
mtvec

Reputation: 18316

If you want to see the assembly output, use this:

g++ -S file.cpp

If you want to see some (pseudo) C++ code that GCC generates, you can use this:

g++ -fdump-tree-original file.cpp

For your add function, this will output something like

;; Function T add(const T&, const T&) [with T = int] (null)
;; enabled by -tree-original

return <retval> = (int) *l + (int) *r;

(I passed the parameters by reference to make the output a little more interesting)

Upvotes: 40

qdot
qdot

Reputation: 6335

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)

Upvotes: 23

Johann Gerell
Johann Gerell

Reputation: 25581

When the optimizer has done its deeds, you most likely have nothing left that looks like a function call. In your specific example, you'll definitely end up with an inlined addition, at worse. Other than that, you can always emit the generated assembler in a separate file during compilation, and there lies your answer.

Upvotes: 3

Johan Kotlinski
Johan Kotlinski

Reputation: 25739

The easiest is to inspect the generated assembly. You can get an assembly source by using -S flag for g++.

Upvotes: 2

Related Questions