About TypeTitan

A tool that generates type info for C++

In my previous projects I had always wanted some way to get type info, but there was no tool or library that really satisfied all my requirements.

I wanted a tool/library that:

  • Used minimal templates
  • Used minimal macros
  • Had little run-time cost
  • Didn’t use RTTI
  • Didn’t use exceptions

There are a few high-profile libraries that use reflection, but you have to manually register your types with syntax that looks like this, which I just didn’t want.

REGISTER_TYPE(Foo)
    REGISTER_FIELD(field_1),
    REGISTER_FIELD(field_2),
    REGISTER_METHOD(func)
REGISTER_TYPE_END();

I decided to delve in and see if I could make a tool that did all of this with minimal effort from the user’s part. I first came up with the idea of using a macro that stringified the type name to call a function, like so:

typeof(Foo) -> tt::typeof_Foo()

However, this approach had a critical drawback: any type with spaces, like long long, would not work. Additionally, there would be extra effort required to get types from namespaces.

I decided to go with a template-based approach, since then C++ could figure out all the overloads. This approach would also avoid the problem of causing an error if the type was not indexed.

At first I wrote the application in C# but the library I was using, ClangSharp, did not offer all the features of it’s C counterpart, so I decided to rewrite it in C++. The end result is a more feature-complete program that is faster and has a smaller memory footprint.

After comprehensive testing, which probably still isn’t comprehensive enough, I finally got a decent version working, and I hope that anyone who comes across it will like it as much as I do.

Showcase

You can mark types with //!! and they will get picked up by TypeTitan. You can also specify some extra arguments in certain contexts.

//!!
struct POD {
  int i;
  float x;
};

//!!
int some_func(float f = 5.0f) {
  // ...
}

//!!
template<typename T, int size>
class Array {
  T arr[size];
};

Here are some examples of extra arguments:

//!! Tags=SomeTag,OtherTag
struct POD {
  int i;
  float f;
  
  //!! DoNotIndex
  void* ptr;
};

//!!
enum class Fruit {
  Apple,
  Pear,
  //!! DoNotIndex
  Lettuce
};

Then to finally retrieve the type data all you have to do is:

POD pod;

const TypeInfo* pod_ti = type_of<POD>();
const TypeInfo* pod_ti = type_of(pod);

There are two main methods of using the type data. The “manual” method and the “utility” method.

With the manual method, you just have the bare TypeTitan files, and you can use the data of the type info struct as follows:

const TypeInfo* ti = type_of<POD>();

if (ti->type == TypeInfoType::Record) {
  const TypeInfoRecord* tir = (const TypeInfoRecord*)ti;
  for (int i = 0; i < tir->field_count; i++) {
    printf("%s\n", tir->fields[i].name);
  }
}

If you prefer a nicer, more modern way of accessing this data, you can omit the -no-extras flag, and a helper file, type_titan.util.h, will be generated.

for (auto& field : get_fields<POD>()) {
  printf("%s\n", field.name);  
}

TypeTitan output