New Age C++

C++ Introspection

C++ has several methods -- including the <type_traits> header and runtime type information -- to help your application make type-based decisions.

C++ programmers, unlike Java and .NET developers, don't get the benefit of runtime introspection -- the ability to learn about a class's features. However, C++ offers some limited introspection at both compile time and runtime that are worth examining.

The <type_traits> Header
I'll start with compile time. C++11 added a header, <type_traits>, with functions that can ask aspects about a type. This makes sense with template programming (and meta-programming as well), because you're working with generic types; but you still want to make sure the types meet certain premises.

<type_traits> is less granular than Java and .NET introspection APIs, because it doesn't allow you to extract class information via reflection (like method names or its parameters). Its functions help determine things like whether a type is C-compatible (plain-old data), or a class, function, reference, void and so on. Here are a few examples of <type_traits>:

// #include <type_traits>
is_pod<my_type>::value // The expression is true if my_type is a plain
                       // old data type compatible with C
is_array<my_array>::value // True if my_array is a primitive array

is_abstract<my_type>::value // True if my_type declares or inherits
                            // pure virtual functions
is_base_of<my_type_1, my_type_2> // True if my_type_2 derives from
                                 // my_type_1
<type_traits> also helps determine if a type supports certain operators like the default constructor, copy and move assignments, and so on:
is_default_constructible<my_type>::value // The expression is true if
                                         // my_type has an accessible default constructor
is_move_assignable<my_type> // True if my_type has an accessible move
                            // assignment operator
is_destructible<my_type> // True if my_type has a non-deleted destructor
Compile-time assertion checking is a great complement to the functions included in <type_traits>. This is probably why they came together in C++11. The reserved keyword static_assert evaluates a constant expression, usually based on <type_traits> functions (although this isn't mandatory). If the expression is false, a compile error is issued:
static_assert(has_virtual_destructor<my_type>::value,
  "my_type needs a virtual destructor for derived classes to be safely deleted.");

Compile-Time Type Inference
The C++ equivalent of the C# keyword var is auto. This reserved word existed previously, but it was repurposed in C++11. Before, it meant automatic storage duration. In practice, an automatic variable is allocated at the beginning of the enclosing block in which it's declared, and de-allocated at the end. With C++11, a variable declared as auto means to declare that the variable is the type of the expression used to initialize it. In other words, an automatic variable must be initialized or a compile error will be produced:

auto i = 0; // i is int
This keyword is extremely practical, even in cases when you don't need to infer a type, and you just want to avoid typing it:
list<string> hello_list {"hello", "world"};
auto hello_iter = begin(hello_list); // hello_iter type is list<string>::iterator
Auto comes with a complementary new keyword -- decltype -- that declares a variable by inferring its type, but without getting it initialized:
template<class T, class U>
auto add(T t, U u) -> decltype(t + u) // The return type of add is the type of operator+(T,U)
{
  return t + u;
}
// add(1, 1.2) type is double
That covers compile-time introspection. What about runtime?

Runtime Type Information
Runtime introspection, curiously, was around before C++11. Like <type_traits>, it's limited to type names, not class members. Rather than generic programming, runtime type information (RTTI) is useful in polymorphic scenarios when you need to know what type of hierarchy is being pointed to or referred:

// #include <typeinfo>
auto my_array = { "Hello,", "world!" }; // std::initializer_list<char*>
cout << "The type of my_array is " << typeid(my_array).name() << '\n';

Make C++ Types Speak by Themselves
The principle of object orientation is all about abstraction. Consequently, algorithms that act on types should defer to these types to deal with their internals in their way. However, this might be sometimes unavoidable. In generic programming, for instance, you may need to keep some control over the kind of specialization made on a template. The functions declared in the <type_traits> header, especially when combined with static assertions, are effective tools you can use to make decisions at compile time. Likewise, when you need to know the actual type referred to by a pointer or reference, RTTI can assist you.

This level of introspection doesn't equal what you get with managed code, but it can certainly help your application make decisions based on actual instantiated types.

About the Author

Diego Dagum is a software architect and developer with more than 20 years of experience. He can be reached at [email protected]

comments powered by Disqus

Featured

  • VS Code Java Team Details 5 Best Dev Practices

    Microsoft's Visual Studio Code team for Java development added a new Coding Pack for Java installer and detailed best practices for setting up a development environment.

  • Binary Classification Using PyTorch: Defining a Network

    Dr. James McCaffrey of Microsoft Research tackles how to define a network in the second of a series of four articles that present a complete end-to-end production-quality example of binary classification using a PyTorch neural network, including a full Python code sample and data files.

  • Blazor Debugging Boosted in .NET 5 RC 2

    In highlighting updates to ASP.NET Core in the just-launched second and final Release Candidate of .NET 5, Microsoft pointed out better debugging for Blazor, the red-hot project that allows for C# coding of web projects.

  • Block Stack

    Final Go-Live .NET 5 Release Candidate Ships Ahead of Nov. 10 Debut

    Having been deemed "feature complete" and "near final" and "go live" for some time now, .NET 5 is out in a second and final Release Candidate, scheduled for a Nov. 10 debut during .NET Conf 2020.

  • Edge Browser Dev Tools for VS Code Now Generally Available

    Microsoft has moved its Edge browser development tools for Visual Studio Code from preview to general availability, providing in-editor web site debugging and other functionality.

Upcoming Events