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 v1.99 Is All About Copilot Chat AI, Including Agent Mode

    Agent Mode provides an autonomous editing experience where Copilot plans and executes tasks to fulfill requests. It determines relevant files, applies code changes, suggests terminal commands, and iterates to resolve issues, all while keeping users in control to review and confirm actions.

  • Windows Community Toolkit v8.2 Adds Native AOT Support

    Microsoft shipped Windows Community Toolkit v8.2, an incremental update to the open-source collection of helper functions and other resources designed to simplify the development of Windows applications. The main new feature is support for native ahead-of-time (AOT) compilation.

  • New 'Visual Studio Hub' 1-Stop-Shop for GitHub Copilot Resources, More

    Unsurprisingly, GitHub Copilot resources are front-and-center in Microsoft's new Visual Studio Hub, a one-stop-shop for all things concerning your favorite IDE.

  • Mastering Blazor Authentication and Authorization

    At the Visual Studio Live! @ Microsoft HQ developer conference set for August, Rockford Lhotka will explain the ins and outs of authentication across Blazor Server, WebAssembly, and .NET MAUI Hybrid apps, and show how to use identity and claims to customize application behavior through fine-grained authorization.

  • Linear Support Vector Regression from Scratch Using C# with Evolutionary Training

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the linear support vector regression (linear SVR) technique, where the goal is to predict a single numeric value. A linear SVR model uses an unusual error/loss function and cannot be trained using standard simple techniques, and so evolutionary optimization training is used.

Subscribe on YouTube