Skip to content

Latest commit

 

History

History
243 lines (193 loc) · 7.32 KB

README.md

File metadata and controls

243 lines (193 loc) · 7.32 KB

How to use decltype

"auto" can be used to derive the type of many things, but it has limitations. For example, what is the type of this?:

    template <typename T, typename V>
    auto add_two_template (const T a, const V b) {
        return a + b;
    }

Is it T or V? It could be either, or some derived type. This is where decltype comes to the rescue"

    template <typename T, typename V>
    auto add_two_template (const T a, const V b) -> decltype(a + b) {
        return a + b;
    }

You can apply this to lambdas too:

    auto lambda_add_two = ( [](const int a, const int b) { return a + b; } );
    std::cout << typeid(decltype(lambda_add_two)).name() << std::endl;

We are using typeid(...).name() here to print the information, however, this is compiler specific and so the output will be mangled by the compiler.

In the following example we have a demangler (which you can ignore) but it prints slightly more verbose output using g++ abi interface:

#include <cxxabi.h> // needed for abi::__cxa_demangle
#include <iostream>
#include <memory>

//
// See
// https://stackoverflow.com/questions/4939636/function-to-mangle-demangle-functions
//
auto cppDemangle(const char *abiName)
{
  //
  // This function allocates and returns storage in ret
  //
  int   status;
  char *ret = abi::__cxa_demangle(abiName, 0 /* output buffer */, 0 /* length */, &status);

  auto deallocator = ([](char *mem) {
    if (mem)
      free((void *) mem);
  });

  if (status) {
    // 0: The demangling operation succeeded.
    // -1: A memory allocation failure occurred.
    // -2: mangled_name is not a valid name under the C++ ABI mangling rules.
    // -3: One of the arguments is invalid.
    std::unique_ptr< char, decltype(deallocator) > retval(nullptr, deallocator);
  }

  //
  // Create a unique pointer to take ownership of the returned string so it
  // is freed when that pointers goes out of scope
  //
  std::unique_ptr< char, decltype(deallocator) > retval(ret, deallocator);
  return retval;
}

class TheUniverse
{
private:
  int size;

public:
  constexpr TheUniverse(int size) : size(size) {}
  constexpr int get_size() const { return (size); }
};

constexpr auto what_is_the(int meaning, int of)
{
  meaning++;
  return meaning * of;
}

// const works just as well here for all the below
constexpr auto meaning = 5;
constexpr auto of      = 7;
constexpr auto life    = what_is_the(meaning, of);
constexpr auto planets = 100000;
constexpr char the_earth[ life ] {};

//
// Note using constexpr will make this symbol visible in the linker
//
constexpr auto the_universe = TheUniverse(sizeof(the_earth) * planets);

static int add_two(const int a, const int b) { return a + b; }

template < typename T, typename V > auto add_two_template(const T a, const V b) -> decltype(a + b) { return a + b; }

int main(void)
{
  // What is the meaning...:
  std::cout << "the meaning is " << life << std::endl;

  // What type is life...:
  std::cout << typeid(decltype(life)).name() << std::endl;
  std::cout << cppDemangle(typeid(decltype(life)).name()) << std::endl;

  // How big is the earth...:
  std::cout << sizeof(the_earth) << std::endl;

  // What type is the earth...:
  std::cout << typeid(decltype(the_earth)).name() << std::endl;
  std::cout << cppDemangle(typeid(decltype(the_earth)).name()) << std::endl;

  // How big is the universe...:
  std::cout << the_universe.get_size() << std::endl;

  // What type is the universe...:
  std::cout << typeid(decltype(the_universe)).name() << std::endl;
  std::cout << cppDemangle(typeid(decltype(the_universe)).name()) << std::endl;

  auto lambda_add_two = ([](const int a, const int b) { return a + b; });
  // What type is a lambda_add_two...:
  std::cout << typeid(decltype(lambda_add_two)).name() << std::endl;
  std::cout << cppDemangle(typeid(decltype(lambda_add_two)).name()) << std::endl;

  // What type is a lambda_add_two(1, 2)...:
  std::cout << cppDemangle(typeid(decltype(lambda_add_two(1, 2))).name()) << std::endl;

  // What type is add_two()...:
  std::cout << typeid(decltype(add_two)).name() << std::endl;
  std::cout << cppDemangle(typeid(decltype(add_two)).name()) << std::endl;

  // What type is add_two(1, 2)...:
  std::cout << cppDemangle(typeid(decltype(add_two(1, 2))).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template<int, int>()...:
  std::cout << cppDemangle(typeid(decltype(add_two_template< int, int >)).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template<float, int>()...:
  std::cout << cppDemangle(typeid(decltype(add_two_template< float, int >)).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template<int, float>()...:
  std::cout << cppDemangle(typeid(decltype(add_two_template< int, float >)).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template<float, float>()...:
  std::cout << cppDemangle(typeid(decltype(add_two_template< float, float >)).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template((int)1, (int)2)...:
  std::cout << cppDemangle(typeid(decltype(add_two_template((int) 1, (int) 2))).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template((float)1.1, (int)2)...:
  std::cout << cppDemangle(typeid(decltype(add_two_template((float) 1.1, (int) 2))).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template((int)1, (float)2.2)...:
  std::cout << cppDemangle(typeid(decltype(add_two_template((int) 1, (float) 2.2))).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  // What type is add_two_template((float)1.1, (float)2.2)...:
  std::cout << cppDemangle(typeid(decltype(add_two_template((int) 1.1, (float) 2.2))).name()) << std::endl;
  add_two(1, 2); // just to silence compiler warnings

  return 0;
}

To build:

cd decltype
rm -f *.o example
clang -std=c++2a -Werror -g -O3 -fstack-protector-all -ggdb3 -Wall -c -o main.o main.cpp
clang  main.o -lstdc++  -o example
./example

Expected output:

�[31;1;4mWhat is the meaning...:�[0m
the meaning is 42

�[31;1;4mWhat type is life...:�[0m
i
int

�[31;1;4mHow big is the earth...:�[0m
42

�[31;1;4mWhat type is the earth...:�[0m
A42_c
char [42]

�[31;1;4mHow big is the universe...:�[0m
4200000

�[31;1;4mWhat type is the universe...:�[0m
11TheUniverse
TheUniverse

�[31;1;4mWhat type is a lambda_add_two...:�[0m
Z4mainE3$_1
main::$_1

�[31;1;4mWhat type is a lambda_add_two(1, 2)...:�[0m
int

�[31;1;4mWhat type is add_two()...:�[0m
FiiiE
int (int, int)

�[31;1;4mWhat type is add_two(1, 2)...:�[0m
int

�[31;1;4mWhat type is add_two_template()...:�[0m
int (int, int)

�[31;1;4mWhat type is add_two_template()...:�[0m
float (float, int)

�[31;1;4mWhat type is add_two_template()...:�[0m
float (int, float)

�[31;1;4mWhat type is add_two_template()...:�[0m
float (float, float)

�[31;1;4mWhat type is add_two_template((int)1, (int)2)...:�[0m
int

�[31;1;4mWhat type is add_two_template((float)1.1, (int)2)...:�[0m
float

�[31;1;4mWhat type is add_two_template((int)1, (float)2.2)...:�[0m
float

�[31;1;4mWhat type is add_two_template((float)1.1, (float)2.2)...:�[0m
float