Reputation: 16711
Is it possible to obtain variable name or enum value name at compile time/runtime? In particular, namespace::
/class::
/struct::
/union::
-qualified (with adjustable depth, like have UNIX patch unility -p
/--strip=
option). In gcc 4.8.1 I can write:
#include <iostream>
#include <ostream>
#include <cstdlib>
enum class mnemocode
{
fwait,
finit,
fninit,
fstsw,
fnstsw,
// ...
sahf,
ret,
prologue,
epilogue,
sp_inc,
sp_dec,
call
};
inline /* till C++14 cannot be constexpr */
auto mnemocode_name(mnemocode _mnemocode)
{
switch (_mnemocode) {
case mnemocode::fwait : return "fwait";
case mnemocode::finit : return "finit";
case mnemocode::fninit : return "fninit";
case mnemocode::fstsw : return "fstsw";
case mnemocode::fnstsw : return "fnstsw";
// ...
case mnemocode::sahf : return "sahf";
case mnemocode::ret : return "ret";
case mnemocode::prologue : return "prologue";
case mnemocode::epilogue : return "epilogue";
case mnemocode::sp_inc : return "sp_inc";
case mnemocode::sp_dec : return "sp_dec";
case mnemocode::call : return "call";
default : return "[unknown instruction]";
}
}
inline
std::ostream & operator << (std::ostream & out, mnemocode _mnemocode)
{
return out << mnemocode_name(_mnemocode);
}
int main()
{
std::cout << mnemocode::fwait << std::endl; // fwait
return EXIT_SUCCESS;
}
But I want to be able to do the following:
template< typename M, typename = typename std::enable_if< std::is_same< mnemocode, typename std::remove_reference< typename std::remove_cv< M >::type >::type >::value >
inline constexpr
auto mnemocode_name(M && _mnemocode)
{
constexpr auto const depth = std::numeric_limits< std::size_t >::max(); // remove all qualifiers before last operator ::
return abi::__get_value_info(_mnemocode).name(depth); // compile time if M is constexpr
}
by means of some imaginary constexpr abi::__get_value_info(symbol)
class.
GCC allows me to write:
#include <iostream>
#include <string>
#include <type_traits>
#include <cxxabi.h>
#include <cstdlib>
template< typename T >
/* cannot be constexpr :( */
const char * this_type()
{
return __PRETTY_FUNCTION__;
}
template< typename T >
std::string theirs_type()
{
int status = 0;
auto realname_(abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status));
switch (status) {
case -1: return "Could not allocate memory";
case -2: return "Invalid name under the C++ ABI mangling rules";
case -3: return "Invalid argument to demangle";
}
std::string os(realname_);
std::free(realname_);
if (std::is_volatile< T >::value) {
os = "volatile " + os;
}
if (std::is_const< T >::value) {
os += " const";
}
if (std::is_rvalue_reference< T >::value) {
os += " &&";
} else if (std::is_lvalue_reference< T >::value) {
os += " &";
}
return os;
}
int main()
{
std::cout << this_type< decltype(static_cast< double const >(double())) >() << std::endl; // const char* this_type() [with T = double]
std::cout << theirs_type< double const && >() << std::endl; // double &&
return EXIT_SUCCESS;
}
But this is only about the type names and too far from compile time deduction.
I guess that's all I come up here, would require almost embedded into my executable debugger and the availability of the section with the debug information. But I still do not think, that it's impossible at all.
Upvotes: 0
Views: 1803
Reputation: 8237
There may be other solutions but the way I normally do this is to play with #defines
1) define your enums in a file (say enumdef.h)
ENUMDEF(fwait)
ENUMDEF(finit)
...
ENUMDEF(call)
2) Declare a header for the enums
#ifndef EnumDefEnum_h
#define EnumDefEnum_h
enum mnemcode
{
#define ENUMDEF(x) x,
#include "enumdef.h"
#undef ENUMDEF
mnemcodeMax
};
#endif
3) To print them
#include "enumdefenum.h"
static const char* mnem_str[] =
{
#define ENUMDEF(x) #x,
#include "enumdef.h"
#undef ENUMDEF
""
};
const char* mnem_name(mnemcode index)
{
return mnem_str[index];
}
4) You could even use them in switch statements if you keep to a naming convention
switch (index)
{
// Put in a pragma to tell you which one it is because the compiler will
// tell you the line number in enumdef.h: not the one in this file
#pragma message("switch statement in some routine")
#define ENUMDEF(x) \
case x: Do##x(whatever); break;
#include "enumdef.h"
#undef ENUMDEF
}
Upvotes: 1