diff --git a/build/libaegisub/libaegisub.vcxproj b/build/libaegisub/libaegisub.vcxproj
index 9b46a2277..370c3177a 100644
--- a/build/libaegisub/libaegisub.vcxproj
+++ b/build/libaegisub/libaegisub.vcxproj
@@ -108,6 +108,7 @@
+
diff --git a/build/libaegisub/libaegisub.vcxproj.filters b/build/libaegisub/libaegisub.vcxproj.filters
index dd843fe13..7288ab0e1 100644
--- a/build/libaegisub/libaegisub.vcxproj.filters
+++ b/build/libaegisub/libaegisub.vcxproj.filters
@@ -80,6 +80,9 @@
Header Files
+
+ Header Files
+
Header Files
@@ -373,4 +376,4 @@
Header Files
-
\ No newline at end of file
+
diff --git a/build/tests/tests.vcxproj.filters b/build/tests/tests.vcxproj.filters
index c25a592f9..ebc82f7d6 100644
--- a/build/tests/tests.vcxproj.filters
+++ b/build/tests/tests.vcxproj.filters
@@ -71,6 +71,9 @@
Tests
+
+ Tests
+
Tests
diff --git a/libaegisub/include/libaegisub/type_name.h b/libaegisub/include/libaegisub/type_name.h
new file mode 100644
index 000000000..e777404bf
--- /dev/null
+++ b/libaegisub/include/libaegisub/type_name.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2014, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+
+namespace agi {
+// Get the name of a type as a string in C syntax
+// Currently supports primitives, pointers, references, const, and function pointers
+template struct type_name;
+
+#define AGI_TYPE_NAME_PRIMITIVE(type) \
+ template<> struct type_name { static const char *name() { return #type; }}
+
+AGI_TYPE_NAME_PRIMITIVE(bool);
+AGI_TYPE_NAME_PRIMITIVE(char);
+AGI_TYPE_NAME_PRIMITIVE(double);
+AGI_TYPE_NAME_PRIMITIVE(float);
+AGI_TYPE_NAME_PRIMITIVE(int);
+AGI_TYPE_NAME_PRIMITIVE(void);
+
+#undef AGI_TYPE_NAME_PRIMITIVE
+
+#define AGI_TYPE_NAME_MODIFIER(pre, type) \
+ template \
+ struct type_name { \
+ static std::string name() { \
+ return std::string(type_name::name()) + pre #type; \
+ } \
+ }
+
+AGI_TYPE_NAME_MODIFIER("", *);
+AGI_TYPE_NAME_MODIFIER("", &);
+AGI_TYPE_NAME_MODIFIER(" ", const);
+
+#undef AGI_TYPE_NAME_MODIFIER
+
+template
+std::string function_args(bool is_first) {
+ return std::string(is_first ? "" : ", ") + type_name::name() + ")";
+}
+
+template
+std::string function_args(bool is_first) {
+ return std::string(is_first ? "" : ", ") + type_name::name() + function_args(false);
+}
+
+template
+struct type_name {
+ static std::string name() {
+ return std::string(type_name::name()) + " (*)(" + function_args(true);
+ }
+};
+
+template
+struct type_name {
+ static std::string name() {
+ return std::string(type_name::name()) + " (*)()";
+ }
+};
+}
diff --git a/tests/tests/type_name.cpp b/tests/tests/type_name.cpp
new file mode 100644
index 000000000..d624c5c73
--- /dev/null
+++ b/tests/tests/type_name.cpp
@@ -0,0 +1,34 @@
+// Copyright (c) 2014, Thomas Goyne
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+//
+// Aegisub Project http://www.aegisub.org/
+
+#include
+#include
+
+#include
+
+TEST(lagi_type_name, type_name) {
+ EXPECT_STREQ("bool", agi::type_name::name());
+ EXPECT_EQ("char const", agi::type_name::name());
+ EXPECT_EQ("void*", agi::type_name::name());
+ EXPECT_EQ("float const*", agi::type_name::name());
+ EXPECT_EQ("int const&", agi::type_name::name());
+ EXPECT_EQ("double&", agi::type_name::name());
+ EXPECT_EQ("void (*)()", agi::type_name::name());
+ EXPECT_EQ("int (*)(bool)", agi::type_name::name());
+ EXPECT_EQ("char const* (*)(int, bool, int)", agi::type_name::name());
+ EXPECT_EQ("char* (*)(int, int, void (*)())", agi::type_name::name());
+}
+