Skip to content

Instantly share code, notes, and snippets.

@deplinenoise
Forked from Aszarsha/typesafe_varargs.cpp
Last active December 21, 2015 13:48
Show Gist options
  • Save deplinenoise/6314782 to your computer and use it in GitHub Desktop.
Save deplinenoise/6314782 to your computer and use it in GitHub Desktop.
// Type-safe varargs with C++11 variadic templates.
//
// Andreas Fredriksson <deplinenoise at gmail dott com>
// minor changes from:
// Thomas Hume <thomas dot hume at labri dot fr>
//
// This code is in the public domain.
#include <stdio.h>
#include <stdarg.h>
#include <utility>
enum {
kTypeInt = 1,
kTypeFloat = 2,
kTypeString = 3,
kTypeVec3Ptr = 4,
};
struct Vec3 { float x, y, z; };
template <typename T> struct TypeId { enum { Value = 0 }; };
template <> struct TypeId<int> { enum { Value = kTypeInt }; };
template <> struct TypeId<float> { enum { Value = kTypeFloat }; };
template <> struct TypeId<const char*> { enum { Value = kTypeString }; };
template <> struct TypeId<Vec3*> { enum { Value = kTypeVec3Ptr }; };
template <> struct TypeId<const Vec3*> { enum { Value = kTypeVec3Ptr }; };
template <typename T>
struct TypeIdHelper {
static_assert( TypeId< T >::Value, "T is invalid vararg parameter type" );
enum { Value = TypeId< T >::Value };
};
template <typename... Args>
struct TypeIdArray
{
enum { kCount = sizeof...(Args) };
static int kValues[sizeof...(Args)];
};
template <typename... Args>
int TypeIdArray<Args...>::kValues[sizeof...(Args)] = { TypeIdHelper<Args>::Value... };
template <typename T, typename... Args>
TypeIdArray<T, Args...> TypeIdArrayHelper(T, Args...);
void __attribute__((noinline)) DoThing(size_t arg_count, const int arg_types[], ...)
{
printf("Called with %d args\n", (int) arg_count);
va_list args;
va_start(args, arg_types);
for (size_t i = 0; i < arg_count; ++i) {
switch (arg_types[i]) {
case kTypeInt: printf("An integer: %d\n", va_arg(args, int)); break;
case kTypeFloat: printf("A float: %f\n", va_arg(args, double)); break; // vararg floats go as double
case kTypeString: printf("A string: %s\n", va_arg(args, const char*)); break;
case kTypeVec3Ptr:
{
const Vec3* v = va_arg(args, const Vec3*);
printf("A vec3 ptr: %f %f %f\n", v->x, v->y, v->z);
break;
}
default: printf("Update this switch!");
}
}
va_end(args);
}
// We can avoid calling std::forward here since f is supposed to accepts its parameters
// from variadic ellipsis, hence 'by value' (neigther {l,r}-value reference nor anything fancy)
// (and the range of types it can accept is further diminished by TypeId 'valid' types)
template< typename... Args >
inline __attribute__((always_inline))
void TypedVariadicCall(Args && ... args)
{
typedef decltype(TypeIdArrayHelper( args... )) Helper;
return DoThing( Helper::kCount, Helper::kValues, args... );
}
int main()
{
TypedVariadicCall(1, 2.1f, 1, "foo", "bar");
TypedVariadicCall(1);
TypedVariadicCall("foo", 17);
// DoFunThing(1u); //-- compile-time error, unsigned int not allowed
// DoFunThing(false); //-- compile-time error, bool not allowed
Vec3 bar = { -1.0f, 6.0f, 0.0f };
TypedVariadicCall("here we go", &bar);
// DoFunThing("here we go", bar); //-- compile-time error
int i = 1;
TypedVariadicCall(++i);
printf("i = %d\n", i); // prints 2 - no multiple evaluation
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment