Skip to content

Instantly share code, notes, and snippets.

@Aszarsha
Last active June 15, 2017 21:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Aszarsha/6309106 to your computer and use it in GitHub Desktop.
Save Aszarsha/6309106 to your computer and use it in GitHub Desktop.
Some adjustments to typesafe_varargs from deplinenoise (https://gist.github.com/deplinenoise/6297411) for better error messaging and to remove macro.
// 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 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 F, typename... Args >
inline __attribute__((always_inline))
auto TypedVariadicCall( F && f, Args && ... args )
-> decltype( f( decltype( TypeIdArrayHelper( args... ) )::kCount, decltype( TypeIdArrayHelper( args... ) )::kValues, args... ) )
// C++1y auto return type would be much appreciated ! :-)
{
return f( decltype( TypeIdArrayHelper( args... ) )::kCount, decltype( TypeIdArrayHelper( args... ) )::kValues, args... );
}
int main()
{
TypedVariadicCall(&DoThing, 1, 2.1f, 1, "foo", "bar");
TypedVariadicCall(&DoThing, 1);
TypedVariadicCall(&DoThing, "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(&DoThing, "here we go", &bar);
// DoFunThing("here we go", bar); //-- compile-time error
int i = 1;
TypedVariadicCall(&DoThing, ++i);
printf("i = %d\n", i); // prints 2 - no multiple evaluation
}
@prapin
Copy link

prapin commented Jun 15, 2017

I have published an enhanced version at https://gist.github.com/prapin/5c90b8246e86a2c5d0ede0928cfbdb1d

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment