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.
Related
I'm writing some template classes for parseing some text data files, and as such it is likly the great majority of parse errors will be due to errors in the data file, which are for the most part not written by programmers, and so need a nice message about why the app failed to load e.g. something like:
Error parsing example.txt. Value ("notaninteger")of [MySectiom]Key is not a valid int
I can work out the file, section and key names from the arguments passed to the template function and member vars in the class, however I'm not sure how to get the name of the type the template function is trying to convert to.
My current code looks like, with specialisations for just plain strings and such:
template<typename T> T GetValue(const std::wstring §ion, const std::wstring &key)
{
std::map<std::wstring, std::wstring>::iterator it = map[section].find(key);
if(it == map[section].end())
throw ItemDoesNotExist(file, section, key)
else
{
try{return boost::lexical_cast<T>(it->second);}
//needs to get the name from T somehow
catch(...)throw ParseError(file, section, key, it->second, TypeName(T));
}
}
Id rather not have to make specific overloads for every type that the data files might use, since there are loads of them...
Also I need a solution that does not incur any runtime overhead unless an exception occurs, i.e. a completely compile time solution is what I want since this code is called tons of times and load times are already getting somewhat long.
EDIT: Ok this is the solution I came up with:
I have a types.h containg the following
#pragma once
template<typename T> const wchar_t *GetTypeName();
#define DEFINE_TYPE_NAME(type, name) \
template<>const wchar_t *GetTypeName<type>(){return name;}
Then I can use the DEFINE_TYPE_NAME macro to in cpp files for each type I need to deal with (eg in the cpp file that defined the type to start with).
The linker is then able to find the appropirate template specialisation as long as it was defined somewhere, or throw a linker error otherwise so that I can add the type.
The solution is
typeid(T).name()
where typeid(T) returns std::type_info.
typeid(T).name() is implementation defined and doesn't guarantee human readable string.
Reading cppreference.com :
Returns an implementation defined null-terminated character string
containing the name of the type. No guarantees are given, in
particular, the returned string can be identical for several types and
change between invocations of the same program.
...
With compilers such as gcc and clang, the returned string can be piped through c++filt -t to be converted to human-readable form.
But in some cases gcc doesn't return right string. For example on my machine I have gcc whith -std=c++11 and inside template function typeid(T).name() returns "j" for "unsigned int". It's so called mangled name. To get real type name, use
abi::__cxa_demangle() function (gcc only):
#include <string>
#include <cstdlib>
#include <cxxabi.h>
template<typename T>
std::string type_name()
{
int status;
std::string tname = typeid(T).name();
char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status);
if(status == 0) {
tname = demangled_name;
std::free(demangled_name);
}
return tname;
}
Jesse Beder's solution is likely the best, but if you don't like the names typeid gives you (I think gcc gives you mangled names for instance), you can do something like:
template<typename T>
struct TypeParseTraits;
#define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \
{ static const char* name; } ; const char* TypeParseTraits<X>::name = #X
REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...
And then use it like
throw ParseError(TypeParseTraits<T>::name);
EDIT:
You could also combine the two, change name to be a function that by default calls typeid(T).name() and then only specialize for those cases where that's not acceptable.
As mentioned by Bunkar typeid(T).name is implementation defined.
To avoid this issue you can use Boost.TypeIndex library.
For example:
boost::typeindex::type_id<T>().pretty_name() // human readable
This trick was mentioned under a few other questions, but not here yet.
All major compilers support __PRETTY_FUNC__ (GCC & Clang) /__FUNCSIG__ (MSVC) as an extension.
When used in a template like this:
template <typename T> const char *foo()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
It produces strings in a compiler-dependent format, that contain, among other things, the name of T.
E.g. foo<float>() returns:
"const char* foo() [with T = float]" on GCC
"const char *foo() [T = float]" on Clang
"const char *__cdecl foo<float>(void)" on MSVC
You can easily parse the type names out of those strings. You just need to figure out how many 'junk' characters your compiler inserts before and after the type.
You can even do that completely at compile-time.
The resulting names can slightly vary between different compilers. E.g. GCC omits default template arguments, and MSVC prefixes classes with the word class.
Here's an implementation that I've been using. Everything is done at compile-time.
Example usage:
std::cout << TypeName<float>() << '\n';
std::cout << TypeName<decltype(1.2f)>(); << '\n';
Implementation: (uses C++20, but can be backported; see the edit history for a C++17 version)
#include <algorithm>
#include <array>
#include <cstddef>
#include <string_view>
namespace impl
{
template <typename T>
[[nodiscard]] constexpr std::string_view RawTypeName()
{
#ifndef _MSC_VER
return __PRETTY_FUNCTION__;
#else
return __FUNCSIG__;
#endif
}
struct TypeNameFormat
{
std::size_t junk_leading = 0;
std::size_t junk_total = 0;
};
constexpr TypeNameFormat type_name_format = []{
TypeNameFormat ret;
std::string_view sample = RawTypeName<int>();
ret.junk_leading = sample.find("int");
ret.junk_total = sample.size() - 3;
return ret;
}();
static_assert(type_name_format.junk_leading != std::size_t(-1), "Unable to determine the type name format on this compiler.");
template <typename T>
static constexpr auto type_name_storage = []{
std::array<char, RawTypeName<T>().size() - type_name_format.junk_total + 1> ret{};
std::copy_n(RawTypeName<T>().data() + type_name_format.junk_leading, ret.size() - 1, ret.data());
return ret;
}();
}
template <typename T>
[[nodiscard]] constexpr std::string_view TypeName()
{
return {impl::type_name_storage<T>.data(), impl::type_name_storage<T>.size() - 1};
}
template <typename T>
[[nodiscard]] constexpr const char *TypeNameCstr()
{
return impl::type_name_storage<T>.data();
}
The answer of Logan Capaldo is correct but can be marginally simplified because it is unnecessary to specialize the class every time. One can write:
// in header
template<typename T>
struct TypeParseTraits
{ static const char* name; };
// in c-file
#define REGISTER_PARSE_TYPE(X) \
template <> const char* TypeParseTraits<X>::name = #X
REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...
This also allows you to put the REGISTER_PARSE_TYPE instructions in a C++ file...
As a rephrasing of Andrey's answer:
The Boost TypeIndex library can be used to print names of types.
Inside a template, this might read as follows
#include <boost/type_index.hpp>
#include <iostream>
template<typename T>
void printNameOfType() {
std::cout << "Type of T: "
<< boost::typeindex::type_id<T>().pretty_name()
<< std::endl;
}
If you'd like a pretty_name, Logan Capaldo's solution can't deal with complex data structure: REGISTER_PARSE_TYPE(map<int,int>)
and typeid(map<int,int>).name() gives me a result of St3mapIiiSt4lessIiESaISt4pairIKiiEEE
There is another interesting answer using unordered_map or map comes from https://en.cppreference.com/w/cpp/types/type_index.
#include <iostream>
#include <unordered_map>
#include <map>
#include <typeindex>
using namespace std;
unordered_map<type_index,string> types_map_;
int main(){
types_map_[typeid(int)]="int";
types_map_[typeid(float)]="float";
types_map_[typeid(map<int,int>)]="map<int,int>";
map<int,int> mp;
cout<<types_map_[typeid(map<int,int>)]<<endl;
cout<<types_map_[typeid(mp)]<<endl;
return 0;
}
typeid(uint8_t).name() is nice, but it returns "unsigned char" while you may expect "uint8_t".
This piece of code will return you the appropriate type
#define DECLARE_SET_FORMAT_FOR(type) \
if ( typeid(type) == typeid(T) ) \
formatStr = #type;
template<typename T>
static std::string GetFormatName()
{
std::string formatStr;
DECLARE_SET_FORMAT_FOR( uint8_t )
DECLARE_SET_FORMAT_FOR( int8_t )
DECLARE_SET_FORMAT_FOR( uint16_t )
DECLARE_SET_FORMAT_FOR( int16_t )
DECLARE_SET_FORMAT_FOR( uint32_t )
DECLARE_SET_FORMAT_FOR( int32_t )
DECLARE_SET_FORMAT_FOR( float )
// .. to be exptended with other standard types you want to be displayed smartly
if ( formatStr.empty() )
{
assert( false );
formatStr = typeid(T).name();
}
return formatStr;
}
I just leave it there.
If someone will still need it, then you can use this:
template <class T>
bool isString(T* t) { return false; } // normal case returns false
template <>
bool isString(char* t) { return true; } // but for char* or String.c_str() returns true
.
.
.
This will only CHECK type not GET it and only for 1 type or 2.
There're many good answers here, but I'd guess the easiest is to use a library, isn't it?
You can use this tiny lib, it's pretty cross-platform (tested with recent GCC/Clang/MSVC/ICC), supports C++11 onwards, works at compile time (consteval since C++20) and in theory it's going to support every compiler since the C++20's <source_location> gains wider support. A few more notes are in the README :)
I found this trick to publish a service interface with type information
#include <iostream>
using namespace std;
const char* nameOfType(int& ){ return "int";}
const char* nameOfType(const char* &){ return "string";}
const char* nameOfType(float& ){ return "float";}
const char* nameOfType(double& ){ return "double";}
template <typename T>
const char* templateFunction(T t){
return nameOfType(t) ;
};
int main()
{
cout<<"Hello World this is an ";
cout << templateFunction<int>(1) << std::endl;
cout << templateFunction<const char*>("") << std::endl;
cout << templateFunction<double>(3.14) <<std::endl;
return 0;
}
Hope it can help somebody.
Complementing the awesome answer by #HolyBlackCat which does the job at compile time. I've managed to encapsulate the logic inside a struct so there's no need for a "impl" namespace, instead the 'impl' functions are guarded in a private manner.
template<typename T>
struct TypeInfo
{
private:
struct RawTypeNameFormat
{
std::size_t leading_junk = 0;
std::size_t trailing_junk = 0;
};
template<typename U>
static constexpr const auto& RawTypeName()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
// Returns `false` on failure.
static constexpr inline bool GetRawTypeNameFormat(RawTypeNameFormat *format)
{
const auto &str = RawTypeName<int>();
for (std::size_t i = 0;; i++)
{
if (str[i] == 'i' && str[i+1] == 'n' && str[i+2] == 't')
{
if (format)
{
format->leading_junk = i;
format->trailing_junk = sizeof(str)-i-3-1; // `3` is the length of "int", `1` is the space for the null terminator.
}
return true;
}
}
return false;
}
static constexpr inline RawTypeNameFormat format =
[]{
static_assert(GetRawTypeNameFormat(nullptr), "Unable to figure out how to generate type names on this compiler.");
RawTypeNameFormat format;
GetRawTypeNameFormat(&format);
return format;
}();
// Returns the type name in a `std::array<char, N>` (null-terminated).
[[nodiscard]]
static constexpr auto GetTypeName()
{
constexpr std::size_t len = sizeof(RawTypeName<T>()) - format.leading_junk - format.trailing_junk;
std::array<char, len> name{};
for (std::size_t i = 0; i < len - 1; i++)
name[i] = RawTypeName<T>()[i + format.leading_junk];
return name;
}
public:
[[nodiscard]]
static cstring Name()
{
static constexpr auto name = GetTypeName();
return name.data();
}
[[nodiscard]]
static cstring Name(const T&)
{
return name();
}
}
Example usage:
auto myTypeName = TypeInfo<MyType>::Name();
or:
const char* myTypeName = TypeInfo<MyType>::Name();
Since c++20 we can use std::source_location::function_name() to get a string containing the function name and arguments.
template<typename T>
consteval auto type_name()
{
std::string_view func_name(std::source_location::current().function_name()); // returns something like: consteval auto type_name() [with T = int]
auto extracted_params = ... Do some post processing here to extract the parameter names.
return extracted_params;
}
N.B.: at the time of writing (Oct 2022) MSVC does not report the template parameters, so this solution will not work there. Unfortunately, the form of the return value of function_name() isn't specified in the standard, but we may at least hope that they will add the template parameters in later versions.
Example
I am writing a template function like that:
template<typename T>
std::string EncodeData(int DataType, T Data, std::string ReadCommandID, std::string& ThisID);
The type T that I want to use is some different structs, for example:
struct A
{
int A_data;
bool A_data_2;
// etc.......
};
struct B
{
double B_data;
char B_data_2;
// etc.......
};
I want the function can access different member variables depends on different struct passing to T,so I wrote such code:
template<typename T>
std::string EncodeData(int DataType, T Data, std::string ReadCommandID, std::string& ThisID)
{
if(std::is_same<T, A>{})
{
// Do something with Data.A_data and Data.A_data_2
}
else if(std::is_same<T, B>{})
{
// Do something with Data.B_data and Data.B_data_2
}
// other code.
}
And use it:
A data={100,false};
std::string id;
std::string result = EncodeData<A>(0,data,"read_id_xxxxxxxx",id);
But when I compile it, the following error happened:
error C2039: "B_data": is not a member of "A".
error C2039: "B_data_2": is not a member of "A".
How can I fix this? Or what else I can do to solve this problem in one single function?
P.S. I'm using MSVC Compiler(Visual Studio 2019)
What else I can do to slove this problom in one single function?
This is not possible under c++11 compiler. However, in C++17 you have if constexpr to do such:
template<typename T>
std::string EncodeData(int DataType, T const& Data, std::string const& ReadCommandID, std::string& ThisID)
{
if constexpr (std::is_same_v<T, A>)
{
// Do something with Data.A_data and Data.A_data_2
}
else if constexpr (std::is_same_v<T, B>)
{
// Do something with Data.B_data and Data.B_data_2
}
}
For C++11 you still need two functions. Therefore, I would suggest having a function overload for each, which might be more readable than having templates.
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.
I have a template function specified:
template<class T> void jsonParse(json_object* jobj, eParseWhat parseWhat, T &value) {
...
if(parseWhat == PARSE_UID) {
value = json_object_getInt(val);
}
if(parseWhar == PARSE_EMAIL) {
value = json_object_getString(val);
}
...
}
Now when I want to parse the uid of the json-object I call the method with an int:
json_object* obj = ...;
int uid = 0;
jsonParse(obj,PARSE_UID,uid);
But then a compile error occurs at the assignment in line:
value = json_object_getString(val);
Because of the call with an int the compiler thinks that the type of the variable value is int and json_object_getString(val) returns const char*. So the compiler says:
can not convert from const char* to int.
Have you got any suggestion to solve this problem?
Why are you even using a template if you are going to do a switch statement of a bunch of if statements for each type? Your template instantiation will never compile if you want to treat every type as multiple types. This design is flawed but if you must then you can use specializations to accomplish something similar.
template<class T> void jsonParse(json_object* jobj, eParseWhat parseWhat, T &value) {
static_assert(false); // not a handled type
}
template<> void jsonParse(json_object* jobj, eParseWhat parseWhat, int &value) {
value = json_object_getInt(val);
}
template<> void jsonParse(json_object* jobj, eParseWhat parseWhat, std::string &value) {
value = json_object_getString(val);
}
As GMan points out it is usually preferable to overload functions rather than specialize a function template. The equivalent using an overloaded function would be something like this:
void jsonParse(json_object* jobj, eParseWhat parseWhat, int &value) {
value = json_object_getInt(val);
}
void jsonParse(json_object* jobj, eParseWhat parseWhat, std::string &value) {
value = json_object_getString(val);
}
Your code won't compile in any way, cause in that method T value can't be int and char* at the same time...
You try to cast differnt types (one is a int, on is a char) to a value (that isn't even defined) ;)
First you have to use:
json_object* obj = ...;
int uid = 0;
jsonParse<int>(obj,PARSE_UID,uid); //last parameter is int
or
jsonParse<string>(obj,PARSE_UID,whatever); //last parameter is string
But that don't work, as said before... you can't use a type in two diffrent ways in one template...
Sorry... :)
Didn't you intend to write:
if(parseWhat == PARSE_UID) {
value = json_object_getInt(val);
}
else if(parseWhar == PARSE_EMAIL) {
value = json_object_getString(val);
}
On a sidenote: template usage in your example is quite strange and confusing to me. I'd rather use some kind of Variant class for this (one example is boost::any, but you can easily implement your own - it's a matter of two hours).
I'm writing some template classes for parseing some text data files, and as such it is likly the great majority of parse errors will be due to errors in the data file, which are for the most part not written by programmers, and so need a nice message about why the app failed to load e.g. something like:
Error parsing example.txt. Value ("notaninteger")of [MySectiom]Key is not a valid int
I can work out the file, section and key names from the arguments passed to the template function and member vars in the class, however I'm not sure how to get the name of the type the template function is trying to convert to.
My current code looks like, with specialisations for just plain strings and such:
template<typename T> T GetValue(const std::wstring §ion, const std::wstring &key)
{
std::map<std::wstring, std::wstring>::iterator it = map[section].find(key);
if(it == map[section].end())
throw ItemDoesNotExist(file, section, key)
else
{
try{return boost::lexical_cast<T>(it->second);}
//needs to get the name from T somehow
catch(...)throw ParseError(file, section, key, it->second, TypeName(T));
}
}
Id rather not have to make specific overloads for every type that the data files might use, since there are loads of them...
Also I need a solution that does not incur any runtime overhead unless an exception occurs, i.e. a completely compile time solution is what I want since this code is called tons of times and load times are already getting somewhat long.
EDIT: Ok this is the solution I came up with:
I have a types.h containg the following
#pragma once
template<typename T> const wchar_t *GetTypeName();
#define DEFINE_TYPE_NAME(type, name) \
template<>const wchar_t *GetTypeName<type>(){return name;}
Then I can use the DEFINE_TYPE_NAME macro to in cpp files for each type I need to deal with (eg in the cpp file that defined the type to start with).
The linker is then able to find the appropirate template specialisation as long as it was defined somewhere, or throw a linker error otherwise so that I can add the type.
The solution is
typeid(T).name()
where typeid(T) returns std::type_info.
typeid(T).name() is implementation defined and doesn't guarantee human readable string.
Reading cppreference.com :
Returns an implementation defined null-terminated character string
containing the name of the type. No guarantees are given, in
particular, the returned string can be identical for several types and
change between invocations of the same program.
...
With compilers such as gcc and clang, the returned string can be piped through c++filt -t to be converted to human-readable form.
But in some cases gcc doesn't return right string. For example on my machine I have gcc whith -std=c++11 and inside template function typeid(T).name() returns "j" for "unsigned int". It's so called mangled name. To get real type name, use
abi::__cxa_demangle() function (gcc only):
#include <string>
#include <cstdlib>
#include <cxxabi.h>
template<typename T>
std::string type_name()
{
int status;
std::string tname = typeid(T).name();
char *demangled_name = abi::__cxa_demangle(tname.c_str(), NULL, NULL, &status);
if(status == 0) {
tname = demangled_name;
std::free(demangled_name);
}
return tname;
}
Jesse Beder's solution is likely the best, but if you don't like the names typeid gives you (I think gcc gives you mangled names for instance), you can do something like:
template<typename T>
struct TypeParseTraits;
#define REGISTER_PARSE_TYPE(X) template <> struct TypeParseTraits<X> \
{ static const char* name; } ; const char* TypeParseTraits<X>::name = #X
REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...
And then use it like
throw ParseError(TypeParseTraits<T>::name);
EDIT:
You could also combine the two, change name to be a function that by default calls typeid(T).name() and then only specialize for those cases where that's not acceptable.
As mentioned by Bunkar typeid(T).name is implementation defined.
To avoid this issue you can use Boost.TypeIndex library.
For example:
boost::typeindex::type_id<T>().pretty_name() // human readable
This trick was mentioned under a few other questions, but not here yet.
All major compilers support __PRETTY_FUNC__ (GCC & Clang) /__FUNCSIG__ (MSVC) as an extension.
When used in a template like this:
template <typename T> const char *foo()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
It produces strings in a compiler-dependent format, that contain, among other things, the name of T.
E.g. foo<float>() returns:
"const char* foo() [with T = float]" on GCC
"const char *foo() [T = float]" on Clang
"const char *__cdecl foo<float>(void)" on MSVC
You can easily parse the type names out of those strings. You just need to figure out how many 'junk' characters your compiler inserts before and after the type.
You can even do that completely at compile-time.
The resulting names can slightly vary between different compilers. E.g. GCC omits default template arguments, and MSVC prefixes classes with the word class.
Here's an implementation that I've been using. Everything is done at compile-time.
Example usage:
std::cout << TypeName<float>() << '\n';
std::cout << TypeName<decltype(1.2f)>(); << '\n';
Implementation: (uses C++20, but can be backported; see the edit history for a C++17 version)
#include <algorithm>
#include <array>
#include <cstddef>
#include <string_view>
namespace impl
{
template <typename T>
[[nodiscard]] constexpr std::string_view RawTypeName()
{
#ifndef _MSC_VER
return __PRETTY_FUNCTION__;
#else
return __FUNCSIG__;
#endif
}
struct TypeNameFormat
{
std::size_t junk_leading = 0;
std::size_t junk_total = 0;
};
constexpr TypeNameFormat type_name_format = []{
TypeNameFormat ret;
std::string_view sample = RawTypeName<int>();
ret.junk_leading = sample.find("int");
ret.junk_total = sample.size() - 3;
return ret;
}();
static_assert(type_name_format.junk_leading != std::size_t(-1), "Unable to determine the type name format on this compiler.");
template <typename T>
static constexpr auto type_name_storage = []{
std::array<char, RawTypeName<T>().size() - type_name_format.junk_total + 1> ret{};
std::copy_n(RawTypeName<T>().data() + type_name_format.junk_leading, ret.size() - 1, ret.data());
return ret;
}();
}
template <typename T>
[[nodiscard]] constexpr std::string_view TypeName()
{
return {impl::type_name_storage<T>.data(), impl::type_name_storage<T>.size() - 1};
}
template <typename T>
[[nodiscard]] constexpr const char *TypeNameCstr()
{
return impl::type_name_storage<T>.data();
}
The answer of Logan Capaldo is correct but can be marginally simplified because it is unnecessary to specialize the class every time. One can write:
// in header
template<typename T>
struct TypeParseTraits
{ static const char* name; };
// in c-file
#define REGISTER_PARSE_TYPE(X) \
template <> const char* TypeParseTraits<X>::name = #X
REGISTER_PARSE_TYPE(int);
REGISTER_PARSE_TYPE(double);
REGISTER_PARSE_TYPE(FooClass);
// etc...
This also allows you to put the REGISTER_PARSE_TYPE instructions in a C++ file...
As a rephrasing of Andrey's answer:
The Boost TypeIndex library can be used to print names of types.
Inside a template, this might read as follows
#include <boost/type_index.hpp>
#include <iostream>
template<typename T>
void printNameOfType() {
std::cout << "Type of T: "
<< boost::typeindex::type_id<T>().pretty_name()
<< std::endl;
}
If you'd like a pretty_name, Logan Capaldo's solution can't deal with complex data structure: REGISTER_PARSE_TYPE(map<int,int>)
and typeid(map<int,int>).name() gives me a result of St3mapIiiSt4lessIiESaISt4pairIKiiEEE
There is another interesting answer using unordered_map or map comes from https://en.cppreference.com/w/cpp/types/type_index.
#include <iostream>
#include <unordered_map>
#include <map>
#include <typeindex>
using namespace std;
unordered_map<type_index,string> types_map_;
int main(){
types_map_[typeid(int)]="int";
types_map_[typeid(float)]="float";
types_map_[typeid(map<int,int>)]="map<int,int>";
map<int,int> mp;
cout<<types_map_[typeid(map<int,int>)]<<endl;
cout<<types_map_[typeid(mp)]<<endl;
return 0;
}
typeid(uint8_t).name() is nice, but it returns "unsigned char" while you may expect "uint8_t".
This piece of code will return you the appropriate type
#define DECLARE_SET_FORMAT_FOR(type) \
if ( typeid(type) == typeid(T) ) \
formatStr = #type;
template<typename T>
static std::string GetFormatName()
{
std::string formatStr;
DECLARE_SET_FORMAT_FOR( uint8_t )
DECLARE_SET_FORMAT_FOR( int8_t )
DECLARE_SET_FORMAT_FOR( uint16_t )
DECLARE_SET_FORMAT_FOR( int16_t )
DECLARE_SET_FORMAT_FOR( uint32_t )
DECLARE_SET_FORMAT_FOR( int32_t )
DECLARE_SET_FORMAT_FOR( float )
// .. to be exptended with other standard types you want to be displayed smartly
if ( formatStr.empty() )
{
assert( false );
formatStr = typeid(T).name();
}
return formatStr;
}
I just leave it there.
If someone will still need it, then you can use this:
template <class T>
bool isString(T* t) { return false; } // normal case returns false
template <>
bool isString(char* t) { return true; } // but for char* or String.c_str() returns true
.
.
.
This will only CHECK type not GET it and only for 1 type or 2.
There're many good answers here, but I'd guess the easiest is to use a library, isn't it?
You can use this tiny lib, it's pretty cross-platform (tested with recent GCC/Clang/MSVC/ICC), supports C++11 onwards, works at compile time (consteval since C++20) and in theory it's going to support every compiler since the C++20's <source_location> gains wider support. A few more notes are in the README :)
I found this trick to publish a service interface with type information
#include <iostream>
using namespace std;
const char* nameOfType(int& ){ return "int";}
const char* nameOfType(const char* &){ return "string";}
const char* nameOfType(float& ){ return "float";}
const char* nameOfType(double& ){ return "double";}
template <typename T>
const char* templateFunction(T t){
return nameOfType(t) ;
};
int main()
{
cout<<"Hello World this is an ";
cout << templateFunction<int>(1) << std::endl;
cout << templateFunction<const char*>("") << std::endl;
cout << templateFunction<double>(3.14) <<std::endl;
return 0;
}
Hope it can help somebody.
Complementing the awesome answer by #HolyBlackCat which does the job at compile time. I've managed to encapsulate the logic inside a struct so there's no need for a "impl" namespace, instead the 'impl' functions are guarded in a private manner.
template<typename T>
struct TypeInfo
{
private:
struct RawTypeNameFormat
{
std::size_t leading_junk = 0;
std::size_t trailing_junk = 0;
};
template<typename U>
static constexpr const auto& RawTypeName()
{
#ifdef _MSC_VER
return __FUNCSIG__;
#else
return __PRETTY_FUNCTION__;
#endif
}
// Returns `false` on failure.
static constexpr inline bool GetRawTypeNameFormat(RawTypeNameFormat *format)
{
const auto &str = RawTypeName<int>();
for (std::size_t i = 0;; i++)
{
if (str[i] == 'i' && str[i+1] == 'n' && str[i+2] == 't')
{
if (format)
{
format->leading_junk = i;
format->trailing_junk = sizeof(str)-i-3-1; // `3` is the length of "int", `1` is the space for the null terminator.
}
return true;
}
}
return false;
}
static constexpr inline RawTypeNameFormat format =
[]{
static_assert(GetRawTypeNameFormat(nullptr), "Unable to figure out how to generate type names on this compiler.");
RawTypeNameFormat format;
GetRawTypeNameFormat(&format);
return format;
}();
// Returns the type name in a `std::array<char, N>` (null-terminated).
[[nodiscard]]
static constexpr auto GetTypeName()
{
constexpr std::size_t len = sizeof(RawTypeName<T>()) - format.leading_junk - format.trailing_junk;
std::array<char, len> name{};
for (std::size_t i = 0; i < len - 1; i++)
name[i] = RawTypeName<T>()[i + format.leading_junk];
return name;
}
public:
[[nodiscard]]
static cstring Name()
{
static constexpr auto name = GetTypeName();
return name.data();
}
[[nodiscard]]
static cstring Name(const T&)
{
return name();
}
}
Example usage:
auto myTypeName = TypeInfo<MyType>::Name();
or:
const char* myTypeName = TypeInfo<MyType>::Name();
Since c++20 we can use std::source_location::function_name() to get a string containing the function name and arguments.
template<typename T>
consteval auto type_name()
{
std::string_view func_name(std::source_location::current().function_name()); // returns something like: consteval auto type_name() [with T = int]
auto extracted_params = ... Do some post processing here to extract the parameter names.
return extracted_params;
}
N.B.: at the time of writing (Oct 2022) MSVC does not report the template parameters, so this solution will not work there. Unfortunately, the form of the return value of function_name() isn't specified in the standard, but we may at least hope that they will add the template parameters in later versions.
Example