Get the name of a std::function - c++

In the following toy-example, I would like to get the name of a function. The function itself was given as an std::function argument. Is it possible in C++ to get name of a std::function object?
void printName(std::function<void()> func){
//Need a function name()
std::cout << func.name();
}
void magic(){};
//somewhere in the code
printName(magic());
output: magic
Otherwise I would have to give the function's name as a second parameter.

No there isn't. Function names (like variable names) are compiled out so they are not visible at run-time.
Your best bet is to pass the name of the function (use a std::string or a const char*) as you've suggested yourself. (Alternatively you could base a solution on __func__ which was introduced in C++11.)

The answer is no, but you could make something like
template<class R, class... Args>
class NamedFunction
{
public:
std::string name;
std::function<R(Args...)> func;
NamedFunction(std::string pname, std::function<R(Args...)> pfunc) : name(pname), func(pfunc)
{}
R operator()(Args&&... a)
{
return func(std::forward<Args>(a)...);
}
};
And then define a preprocessor
#define NAMED_FUNCTION(var, type, x) NamedFunction<type> var(#x,x)
...
NAMED_FUNCTION(f, void(), magic);

Given a std::function it has a member function called target_type which returns the typeid of the stored function object. That means you can do
void printName(std::function<void()> func){
//Need a function name()
std::cout << func.target_type().name();
}
This returns an implementation-defined string that is unique for each type. With Visual Studio, this string is human-readable already. With gcc (or maybe it's glibc? I don't know who takes care of what in detail) you need to use abi::__cxa_demangle after including <cxxabi.h> to get a human-readable version of the type name.
EDIT
As Matthieu M. pointed out, given a function pointer, the type returned by this will just be the function's signature. For example:
int function(){return 0;}
printName(function);
This will output (assuming you demangled if necessary) int (*)() which is not the function's name.
This method will work with classes though:
struct Function
{
int operator()(){return 0;}
};
printName(Function{});
This will print Function as desired, but then doesn't work for function pointers.

You could also have your function with a string parameter for the name and then use a macro to call it
void _printName(std::function<void()> func, const std::string& funcName){
std::cout << funcName;
}
#define printName(f) _printName(f, #f)
void magic(){};
//somewhere in the code
printName(magic);
See example

Maintain your own map from function pointer to name.
template<class Sig>
std::map<Sig*, const char*>& name_map() {
static std::map<Sig*, const char*> r;
return r;
}
struct register_name_t {
template<class Sig>
register_name_t( Sig* sig, const char* name ) {
name_map()[sig]=name;
}
};
#define TO_STRING(A) #A
#define REGISTER_NAME(FUNC) \
register_name_t FUNC ## _register_helper_() { \
static register_name_t _{ FUNC, TO_STRING(FUNC) }; \
return _; \
} \
static auto FUNC ## _registered_ = FUNC ## _register_helper_()
Simply do REGISTER_NAME(magic); to register the name magic to the function magic. This should be done at file scope, either in a header or a cpp file.
Now we check if the std::function has a function pointer matching its signature stored inside of it. If so, we look it up in our name_map, and return the name if we find it:
template<class Sig>
std::string get_function_name( std::function<Sig> const& f ) {
auto* ptr = f.target<Sig*>();
if (!ptr) return {};
auto it = name_map().find(ptr);
if (it == name_map().end()) return {};
return it->second;
}
this is generally a bad idea.

I think the simplest solution is to use typeid(fun).name() for example like this:
#include <typeinfo>
#include <stdio.h>
void foobar( void )
{
}
int main()
{
printf( "%s\n", typeid( foobar ).name() );
return 0;
}
Now, it have a lot of drawbacks, and I would not recommend using that.
First of all, IIRC it shows the symbol of the function, not the name you used in source code.
Also, the name will change from compiler to compiler.
And finally, RTTI is slow.
[edit]
Also, I'm not sure how it works with std::function. Never used that, honestly.

Related

Unique id for any kind of callable object in C++17

In one part of my code, I have an abstract function type Function which represents any kind of callable and which can be stored in a heterogeneous container, e.g. std::vector<std::unique_ptr<Function>>:
#include <any>
#include <string>
#include <memory>
#include <vector>
#include <functional>
#include <cassert>
class Function
{
public:
Function(std::string name)
: m_name(name)
{}
virtual ~Function(){}
std::string name() {
return m_name;
}
template <typename... Args>
decltype(auto) operator()(Args&&... args)
{
// delegate to invoke, implementation not relevant for question
}
private:
std::string m_name;
// the following is also simplified for the sake of brevity
virtual std::any invoke(std::initializer_list<std::any> const& args) const = 0;
};
template <typename F>
class FunctionImpl : public Function
{
public:
FunctionImpl(F const& f, std::string name)
: Function(name)
, function(f)
{}
private:
std::any invoke(std::initializer_list<std::any> const& args) const override
{
// implementation not relevant for question
return std::any();
}
F function;
};
using FPointer = std::unique_ptr<Function>;
template <typename F>
FPointer make_function(F const& f, std::string name)
{
return std::make_unique<FunctionImpl<F>>(f, name);
}
Now I want to add a function
using FContainer = std::vector<FPointer>;
template <typename F>
bool contains(FContainer const& vec, F const& f)
{
// ?????
}
which returns true, if the function passed as argument in contained in the container, and false otherwise (and probably in a follow-up step a function that returns a reference to the element in the container, if it is contained). How would I write this kind of function? What are my options?
void bar(){}
void foo(){}
struct AClass {
void MemberFunction1(){}
void MemberFunction2(){}
};
struct ACallableClass
{
void operator()(){}
};
int main()
{
FContainer v;
// function pointer
v.push_back(
make_function(
&foo,
"foo"
)
);
// std::function
v.push_back(
make_function(
std::function<void()>(&foo),
"foo"
)
);
// member function
v.push_back(
make_function(
&AClass::MemberFunction1,
"AClass::MemberFunction1"
)
);
// callable
v.push_back(
make_function(
ACallableClass(),
"CallableClass"
)
);
// lambda
v.push_back(
make_function(
[](){},
"empty lambda"
)
);
assert(contains(v, &foo));
assert(contains(v, std::function<void()>(&foo)));
assert(contains(v, &AClass::MemberFunction1));
assert(!contains(v, [](){})); // every lambda is different
assert(!contains(v, &bar));
assert(!contains(v, std::function<void()>(&bar)));
assert(!contains(v, &AClass::MemberFunction2));
return 0;
}
The best solution I could come up with so far was to write a function template
template <typename F> size_t id(F&& id);
that gives a unique id to any kind of callable. Then Function could get a new virtual size_t id() const = 0 method, which would be overwritten by Function<F>. The latter delegates to the free function template. With this, I could compare ids in contains.
I tried implementing the function template using std::hash with function pointers, but I got stuck at hashing member function pointers, callable classes and lambdas. Here is my latest approach: https://godbolt.org/z/zx4jnYbeG.
Sorry for the rather lengthy question. Any help would be greatly appreciated!
EDIT 1:
I can live without std::function support. I would like to support lambdas in principle, but I can live with contains always returning false for lambdas, which makes sense to me. I do want the code to work with function pointers, callable classes and member functions.
EDIT 2:
Here is a working solution based on the suggestions in xryl669s answer: https://godbolt.org/z/vYGesEsKa. std::function<F> and F get the same id, but I suppose this actually make sense, since they are basically equivalent.
Use an unordered_map and not a vector to store your functions.
The key can be derived from the name (probably better anyway), or from the address of the function, but in that case, you'll have an issue with everything that's not a real function (like a method, a std::function<> instance, a lambda, ...)
But since you probably have an issue already with your make_function for methods (you don't capture the instance), you can probably make a specialization for that case using a lambda or a template trampoline and use that address instead.
Another issue to account for is:
std::function<>(&foo) != std::function<>(&foo) (you have 2 instances, they are 2 different objects)
Similarly for lambda functions, two different instance containing the same lambda body won't match anyway.
Compiler is allowed to generate copies of functions if it has all the code for them and it's doing so unless you build with -Os or use external linkage for your functions
So, unless you fallback to a unique identifier that you assign to your Function, you can't assert that a function is identical to another based on the function's body or some instance.
Example (working) godbolt for the specialized template approach: https://godbolt.org/z/8sP5MfG6r
Please notice that you can't store a &foo and std::function<>(&foo) in the container in this approach if using the std::function<>::target() as the key, they'll point to the same function and thus will be overwritten or not inserted since they already exist, but that's probably a good thing for your application it seems.
If you don't care about UB, you can use this version: https://godbolt.org/z/9GoEWMnMb that's reinterpret_cast'ing the function's pointer (and pointer to method too) to use as the hash's key in the map. That's not clean, but since we don't use the result of the cast to call the function, it shouldn't bother much.

Templating for specific line in function

I'm writing a wrapper for embedded Lua, and I have a series of functions to retrieve global values from a lua_State. Since the function does almost the exact same thing for each (gets the global name with lua_getglobal(L, name), calls the appropriate lua_to___() function, and then pops the stack to return the lua_State to its original status), I figured there should be some way to do this with templates.
However, I can't seem to find a way to have that one specific line where the type matters be dependent on the type without writing completely separate functions for each type. While right now this function would only be three lines long, there are other similar functions that might be more complicated but with the same issue.
As of now, the functions look like this (this is within a class called LuaManager, which has a lua_State* member):
//Declaration
template<std::string>
std::string GetGlobal(const std::string & name);
template<int>
int GetGlobal(const std::string & name);
template<float>
float GetGlobal(const std::string & name);
template<bool>
bool GetGlobal(const std::string & name);
//Implementation
template<typename T>
T LuaManager::GetGlobal(const std::string & name)
{
lua_getglobal(luaState, name.c_str()); //push the global to the top of the stack
T value = lua_to____(luaState, -1); //store the value for return
lua_pop(luaState, 1); //return the stack to empty
return value;
}
Is there a way to be able to, for individual lines of code, specialize a template? Or am I misunderstanding what I should be using templates for?
The declaration should just be:
template<class T>
T GetGlobal(const std::string& name);
And for the implementation, I would create a struct template, and use the specialisations as a map from type to function.
#include <type_traits>
template<class>
struct lua_to;
template<>
struct lua_to<int> {
typedef int(*type)(decltype(luaState), int);
static constexpr const type value = lua_to_int;
};
template<>
struct lua_to<std::string> {
typedef std::string(*type)(decltype(luaState), int);
static constexpr const type value = lua_to_string;
};
// In this case, since this is so tedious, I would use a macro
#define MY_MODULE_DEFINE_LUA_TO(ctype, luatype) \
template<> \
struct lua_to<ctype> { \
typedef ctype(*type)(decltype(luaState), int); \
static constexpr const type value = lua_to_ ## luatype; \
};
MY_MODULE_DEFINE_LUA_TO(std::map, table);
MY_MODULE_DEFINE_LUA_TO(double, float);
#undef MY_MODULE_DEFINE_LUA_TO
template<class T>
T GetGlobal(const std::string& name) {
lua_getglobal(luaState, name); //push the global to the top of the stack
T value = lua_to<T>::value(luaState, -1); //store the value for return
lua_pop(luaState, 1); //return the stack to empty
return value;
}
If your compiler supports C++17, you can use if constexpr:
template<typename T>
T LuaManager::GetGlobal(const std::string & name)
{
lua_getglobal(luaState, name);
T value;
if constexpr (std::is_same_v<T, std::string>)
value = lua_to_string(luaState, -1); // I don't know the actual name of this function
else if (std::is_same_v<T, int>)
value = lua_to_int(luaState, -1);
else if (std::is_same_v<T, whatever>)
value = lua_to_whatever(luaState, -1);
// some other arbitrary type dependent code
else ... // other types
lua_pop(luaState, 1);
return value;
}
Note: To enable C++17 in Visual Studio, right click on your project and click on Properties. Then go to C/C++ -> Language -> C++ Language Standard and select /std:c++17 or /std:c++latest.
Update
If you cannot or don't want to use C++17, here is another approach that doesn't use any "new" features, even without templates:
void get_lua_value(string& value)
{
value = lua_to_string(luaState, -1);
}
void get_lua_value(int& value)
{
value = lua_to_int(luaState, -1);
}
Add one of these overloads for each type. Then you can just call get_lua_value() and overload resolution will do the job for you:
template<typename T>
T LuaManager::GetGlobal(const std::string& name)
{
lua_getglobal(luaState, name);
T value;
get_lua_value(value);
lua_pop(luaState, 1);
return value;
}
I think you should have a generic declaration and specialized implementations in that case. You can't just "switch the function" based on T on that one line. Consider this example:
#include <iostream>
// Generic declaration
template <typename T>
T doStuff(int arg);
// Specific definitions
template<>
int doStuff(int arg){
return arg + 1;
}
template<>
float doStuff(int arg){
return arg - 1;
}
int main(){
// Our templated function varies in return type,
// so you always have to explicitly specify which variant to use
// This WOULD NOT let compile infer what you want:
/* float test = doStuff(10) */ // ambiguous call
// This is OK
std::cout << doStuff<int>(10) << " " << doStuff<float>(10) << "\n";
return 0;
}
You will have an almost-identical GetGlobal function differing in just this one line. If you want to cut the repetition down, you could have a template for a conversion to a C++ type alone (taking a luaState as an arg), and then maybe templated GetGlobal that calls appropriate template variant
So turns out I was trying to make this a bit more complicated than I needed to:
I made the GetGlobal function not care at all about what type it was trying to get:
template<typename T>
T GetGlobal(const std::string & name)
{
lua_getglobal(luaState, name.c_str());
T value = lua_to<T>(-1);
lua_pop(luaState, 1);
return value;
}
And then I defined a template for lua_to<T>(int stack_index) and made each specialization for it use a different function:
template<typename T>
T lua_to(int stack_index);
template<>
int lua_to(int stack_index) {
return lua_tointeger(luaState, stack_index);
}
template<>
std::string lua_to(int stack_index) {
return std::string(lua_tostring(luaState, stack_index));
}
So far it works for both std::string and int, which seems to imply it will work for other types as well.

Coding static to instance method trampoline function with templates

I'm trying to recode some rather ugly template coding.
For reference, the original is here: https://codereview.stackexchange.com/questions/69545/recode-c-c-trampoline-function-macros-using-templates
class Final : Base
{
void Foo(){...}
void Bar(){...}
static void init(){
// register_method populates a table of "extern C" function pointers.
register_method( "foo", & FooHandler );
register_method( "bar", & BarHandler );
}
:
// trampolines
static void FooHandler( void* pInstance ) {
Final* f = reinterpret_cast<Final*>(pInstance);
f->Foo();
}
static void BarHandler( void* pInstance ) {
Final* f = reinterpret_cast<Final*>(pInstance);
f->Bar();
}
}
My code interfaces with CPython (C library).
Python runtime sees "myInst.foo" , looks up "foo" in table and invokes:
Final::FooHandler( pointer_to_myInst );
(Note it is possible to typecast a static method to a C function pointer)
FooHandler trampolines to the Foo method of the correct instance of Final.
In reality, the handle is not so clean, and there are many methods each of which requires an identical handler (but with a distinct function address).
I am attempting to abstract the handler mechanism into a base class, something like this:
class Final : Base<Final>
{
void Foo(){...}
void Bar(){...}
static void init(){
// register_method populates a table of "extern C" function pointers.
register_method( "foo", & Foo, Handler< &Foo> );
register_method( "bar", & Bar, Handler< &Bar> );
}
:
}
class Base<Final>
{
typedef void (Final::*FuncSig)(void);
typedef void (Final::*HandlerSig)(void*); // takes 1 pvoid param
void register_method( std::string name, FuncSig meth, HandlerSig handler ) {
...
}
// generic trampoline
template< Sig sig>
static void Handler( void* pInstance ) {
Final* f = reinterpret_cast<Final*>(pInstance);
f ->* sig();
}
}
I'm currently getting stuck in compiler errors (http://ideone.com/vOtbcD), so I'm not even sure whether the technique is valid.
Is there some way to do this, or is this just one of those times where you really need macros?
For reference, the original is here: https://codereview.stackexchange.com/questions/69545/recode-c-c-trampoline-function-macros-using-templates
As can be seen, the original uses rather ugly macros.
register_method needs to be static if you're going to call it from the static init function.
Calling a member function through a member function pointer requires an additional set of parenthesis: (f->*sig)();
With those in place, your test case compiles in C++11 mode. That said, have you considered using std::function and std::bind to do this instead? Hard to tell what that would actually look like without knowing what register_method does, but it might wind up a bit cleaner.
The following code works on ideone (http://ideone.com/vOtbcD):
#include <iostream>
using namespace std;
#include <map>
template<typename T>
class Base
{
public:
typedef void (T::*PFunc)(void);
typedef void (*PHandler)(void*);
using method_map_t = map< string, PHandler >;
static method_map_t& methods( ) {
static method_map_t* map_of_methods{};
if( ! map_of_methods ) map_of_methods = new method_map_t;
return *map_of_methods;
}
static void register_method( string name, PHandler handler ) {
methods()[name] = handler;
}
// generic trampoline
template<PFunc sig>
static void Handler( void* pInstance ) {
T* f = reinterpret_cast<T*>(pInstance);
(f ->* sig)();
}
};
...
class Final : Base<Final>
{
public:
void Foo(){cout<<"got foo";}
void Bar(){cout<<"got bar";}
static void init(){
// register_method populates a table of "extern C" function pointers.
register_method( "foo", static_cast<PHandler>( &Handler<&Final::Foo> ) );
register_method( "bar", static_cast<PHandler>( &Handler<&Final::Bar> ) );
}
};
void InvokeFromC(void* inst, string name) {
Base<Final>::PHandler h = Base<Final>::methods()[name];
(*h)(inst);
}
int main() {
Final* f = new Final{};
f->init();
// simulate invoking from C
InvokeFromC( f, "foo" );
// your code goes here
return 0;
}
Note: (Eelis on IRC) gcc is known not to be able to resolve addresses of function template specializations passed to functions. You can work around it either by declaring separate variables for the function pointers, or using a dirty cast (or a nice cast like boost::implicit_cast)
I have put this code up for review at: https://codereview.stackexchange.com/questions/69876/static-to-instance-method-trampolining-with-templates

How do I assign an alias to a function name in C++?

It's easy to create a new name for a type, a variable or a namespace. But how do I assign a new name to a function? For example, I want to use the name holler for printf. #define is obvious... any other way?
Solutions:
#define holler printf
void (*p)() = fn; //function pointer
void (&r)() = fn; //function reference
inline void g(){ f(); }
There are different approaches:
With C++11 with non-template non-overloaded functions you can simply use:
const auto& new_fn_name = old_fn_name;
If this function has multiple overloads you should use static_cast:
const auto& new_fn_name = static_cast<OVERLOADED_FN_TYPE>(old_fn_name);
Example: there are two overloads of function std::stoi
int stoi (const string&, size_t*, int);
int stoi (const wstring&, size_t*, int);
If you want to make an alias to the first version you should use the following:
const auto& new_fn_name = static_cast<int(*)(const string&, size_t*, int)>(std::stoi);
Note: there is no way to make an alias to overloaded function such that all its overloaded versions work, so you should always specify which exact function overload you want.
With C++14 you can go even further with constexpr template variables. That allows you to alias templated functions:
template<typename T>
constexpr void old_function(/* args */);
template<typename T>
constexpr auto alias_to_old = old_function<T>;
Moreover, starting with C++11 you have a function called std::mem_fn that allows to alias member functions. See the following example:
struct A {
void f(int i) {
std::cout << "Argument: " << i << '\n';
}
};
A a;
auto greet = std::mem_fn(&A::f); // alias to member function
// prints "Argument: 5"
greet(a, 5); // you should provide an object each time you use this alias
// if you want to bind an object permanently use `std::bind`
greet_a = std::bind(greet, a, std::placeholders::_1);
greet_a(3); // equivalent to greet(a, 3) => a.f(3);
You can create a function pointer or a function reference:
void fn()
{
}
//...
void (*p)() = fn;//function pointer
void (&r)() = fn;//function reference
typedef int (*printf_alias)(const char*, ...);
printf_alias holler = std::printf;
Should do you fine.
int (*holler)(const char*, ...) = std::printf;
Use an inline wrapper. You get both APIs, but keep the single implementation.
With C++14 generic lambdas, I was able to do the following, which should also work when the target function has multiple overloads:
constexpr auto holler = [] ( auto &&...args ) {
return printf( std::forward<decltype(args)>( args )... );
};
From fluentcpp : ALIAS_TEMPLATE_FUNCTION(f, g)
#define ALIAS_TEMPLATE_FUNCTION(highLevelF, lowLevelF) \
template<typename... Args> \
inline auto highLevelF(Args&&... args) -> decltype(lowLevelF(std::forward<Args>(args)...)) \
{ \
return lowLevelF(std::forward<Args>(args)...); \
}
It is worth mentioning here IMO that while the original question (and great answers) are definitely useful if you want to rename a function (there are good reasons to do so!), if all you want to do is strip out a deep namespace but keep the name, there is the using keyword for this:
namespace deep {
namespace naming {
namespace convention {
void myFunction(int a, char b) {}
}
}
}
int main(void){
// A pain to write it all out every time
deep::naming::convention::myFunction(5, 'c');
// Using keyword can be done this way
using deep::naming::convention::myFunction;
myFunction(5, 'c'); // Same as above
}
This also has the advantage of it being confined to a scope, though you could always use it at the top level of a file. I often use this for cout and endl so I don't need to bring in ALL of std with the classic using namespace std; at the top of a file, but also useful if you're using something like std::this_thread::sleep_for() a lot in one file or function, but not everywhere, and not any other functions from the namespace. As always, it's discouraged to use it in .h files, or you'll pollute the global namespace.
This is not the same as the "renaming" above, but is often what is really wanted.

Is it possible to get a char* name from a template type in C++

I want to get the string name (const char*) of a template type. Unfortunately I don't have access to RTTI.
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return /* magic goes here */; }
}
So
SomeClass<int> sc;
sc.GetClassName(); // returns "int"
Is this possible? I can't find a way and am about to give up. Thanks for the help.
No and it will not work reliable with typeid either. It will give you some internal string that depends on the compiler implementation. Something like "int", but also "i" is common for int.
By the way, if what you want is to only compare whether two types are the same, you don't need to convert them to a string first. You can just do
template<typename A, typename B>
struct is_same { enum { value = false }; };
template<typename A>
struct is_same<A, A> { enum { value = true }; };
And then do
if(is_same<T, U>::value) { ... }
Boost already has such a template, and the next C++ Standard will have std::is_same too.
Manual registration of types
You can specialize on the types like this:
template<typename>
struct to_string {
// optionally, add other information, like the size
// of the string.
static char const* value() { return "unknown"; }
};
#define DEF_TYPE(X) \
template<> struct to_string<X> { \
static char const* value() { return #X; } \
}
DEF_TYPE(int); DEF_TYPE(bool); DEF_TYPE(char); ...
So, you can use it like
char const *s = to_string<T>::value();
Of course, you can also get rid of the primary template definition (and keep only the forward declaration) if you want to get a compile time error if the type is not known. I just included it here for completion.
I used static data-members of char const* previously, but they cause some intricate problems, like questions where to put declarations of them, and so on. Class specializations like above solve the issue easily.
Automatic, depending on GCC
Another approach is to rely on compiler internals. In GCC, the following gives me reasonable results:
template<typename T>
std::string print_T() {
return __PRETTY_FUNCTION__;
}
Returning for std::string.
std::string print_T() [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]
Some substr magic intermixed with find will give you the string representation you look for.
The really easy solution: Just pass a string object to the constructor of SomeClass that says what the type is.
Example:
#define TO_STRING(type) #type
SomeClass<int> s(TO_STRING(int));
Simply store it and display it in the implementation of GetClassName.
Slightly more complicated solution, but still pretty easy:
#define DEC_SOMECLASS(T, name) SomeClass<T> name; name.sType = #T;
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return sType.c_str(); }
std::string sType;
};
int main(int argc, char **argv)
{
DEC_SOMECLASS(int, s);
const char *p = s.GetClassName();
return 0;
}
Template non type solution:
You could also make your own type ids and have a function to convert to and from the ID and the string representation.
Then you can pass the ID when you declare the type as a template non-type parameter:
template< typename T, int TYPEID>
struct SomeClass
{
const char* GetClassName() const { return GetTypeIDString(TYPEID); }
};
...
SomeClass<std::string, STRING_ID> s1;
SomeClass<int, INT_ID> s2;
You can try something like this (warning this is just off the top of my head, so there may be compile errors, etc..)
template <typename T>
const char* GetTypeName()
{
STATIC_ASSERT(0); // Not implemented for this type
}
#define STR(x) #x
#define GETTYPENAME(x) str(x) template <> const char* GetTypeName<x>() { return STR(x); }
// Add more as needed
GETTYPENAME(int)
GETTYPENAME(char)
GETTYPENAME(someclass)
template< typename T >
struct SomeClass
{
const char* GetClassName() const { return GetTypeName<T>; }
}
This will work for any type that you add a GETTYPENAME(type) line for. It has the advantage that it works without modifying the types you are interested in, and will work with built-in and pointer types. It does have the distinct disadvantage that you must a line for every type you want to use.
Without using the built-in RTTI, you're going to have to add the information yourself somewhere, either Brian R. Bondy's answer or dirkgently's will work. Along with my answer, you have three different locations to add that information:
At object creation time using SomeClass<int>("int")
In the class using dirkgently's compile-time RTTI or virtual functions
With the template using my solution.
All three will work, it's just a matter of where you'll end up with the least maintenance headaches in your situation.
By you don't have access to RTTI, does that mean you can't use typeid(T).name()? Because that's pretty much the only way to do it with the compiler's help.
Is it very important for the types to have unique names, or are the names going to be persisted somehow? If so, you should consider giving them something more robust than just the name of the class as declared in the code. You can give two classes the same unqualified name by putting them in different namespaces. You can also put two classes with the same name (including namespace qualification) in two different DLLs on Windows, so you need the identify of the DLL to be included in the name as well.
It all depends on what you're going to do with the strings, of course.
You can add a little magic yourself. Something like:
#include <iostream>
#define str(x) #x
#define xstr(x) str(x)
#define make_pre(C) concat(C, <)
#define make_post(t) concat(t, >)
#define make_type(C, T) make_pre(C) ## make_post(T)
#define CTTI_REFLECTION(T, x) static std::string my_typeid() \
{ return xstr(make_type(T, x)); }
// the dark magic of Compile Time Type Information (TM)
#define CTTI_REFLECTION(x) static const char * my_typeid() \
{ return xstr(make_type(T, x)); }
#define CREATE_TEMPLATE(class_name, type) template<> \
struct class_name <type>{ \
CTTI_REFLECTION(class_name, type) \
};
// dummy, we'll specialize from this later
template<typename T> struct test_reflection;
// create an actual class
CREATE_TEMPLATE(test_reflection, int)
struct test_reflection {
CTTI_REFLECTION(test_reflection)
};
int main(int argc, char* argv[])
{
std::cout << test_reflection<int>::my_typeid();
}
I'll make the inspector a static function (and hence non-const).
No, sorry.
And RTTI won't even compile if you try to use it on int.