Z
TO_STRING(X) only return the name X if the value is not defined:std::cout << TO_STRING(__cplusplus) << " " << STRINGIFY(__cplusplus) << '\n'
<< TO_STRING(NO_EXISTO) << " " << STRINGIFY(NO_EXISTO) << '\n';
Departure:201500L __cplusplus
NO_EXISTO NO_EXISTO
A first approach that occurs to me to solve part of the problem is to check if the name has an associated value:template <int SIZE1, int SIZE2 >
constexpr int i(const char (&definition)[SIZE1],const char (&check)[SIZE2])
{
return std::strcmp(definition,check) != 0;
}
Although sincerely, for these cases I prefer to make use of auto:constexpr int i(auto definition, auto check)
{
return std::strcmp(definition,check) != 0;
}
With this we are already able to detect whether the name has been defined or not:#include <iostream>
#include <cstring>
//#define _DEBUG 0
#define DRIVERS 09072007
#define STRINGIFY(X) #X
#define TO_STRING(X) STRINGIFY(X)
#define PARSE(X) TO_STRING(X),#X
constexpr bool i(auto definition, auto check)
{
return std::strcmp(definition,check) != 0;
}
enum stuff : bool
{
cpp_version = i(PARSE(__cplusplus)),
debug_enabled = i(PARSE(DEBUG)),
drivers_version = i(PARSE(DRIVERS)),
};
int main()
{
std::cout << "C++ version: " << stuff::cpp_version << '\n'
<< "Modo debug: " << stuff::debug_enabled << '\n'
<< "Drivers version: " << stuff::drivers_version << '\n';
return 0;
}
Departure:C++ version: 1
Modo debug: 0
Drivers version: 1
The theme of storing the value is somewhat more complex, as with constexpr cannot use the type std::string since he makes use of dynamic memory... I will give him a turn to this point to see if I can think of a way to extract the value associated with the name.This system will fail if the name and value are equal:#define NO_FUNCIONA NO_FUNCIONA
But the chances of this happening I think they're ridiculous.EDITO:After chat conversations and some additional tests I have found a possible solution.For each name there is a pair of values:a boolean indicating whether the name in question is defined or notthe value of the element (to take into account only if the name is defined)Your most recent solution once my changes applied:#include <iostream>
constexpr int power10(int power)
{
if (power == 0)
return 1;
return 10 * power10(power - 1);
}
template<int SIZE_A, int SIZE_B>
constexpr bool same_literals(const char (&a)[SIZE_A], const char (&b)[SIZE_B])
{
bool result = false;
if constexpr( SIZE_A == SIZE_B )
{
result = true;
for (std::int32_t index = 0; result && (index < SIZE_A); ++index)
{
result = (a[index] == b[index]);
}
}
return result;
}
template <int SIZE_A, int SIZE_B>
constexpr std::int32_t integral_value_or_zero(const char (&a)[SIZE_A], const char (&b)[SIZE_B])
{
std::int32_t result = 0;
if ( !same_literals(a, b) )
{
for (std::int32_t index = 0; index < SIZE_B; ++index)
{
if (b[index] >= '0' && b[index] <= '9')
{
result += power10(SIZE_B - index - 2) * (b[index] - '0');
}
}
}
return result;
}
template <int SIZE_A, int SIZE_B>
constexpr bool bool_value_or_false(const char (&a)[SIZE_A], const char (&b)[SIZE_B])
{
return integral_value_or_zero(a,b) != 0;
}
template <int SIZE_A, int SIZE_B>
constexpr const char *string_or_empty(const char (&a)[SIZE_A], const char (&b)[SIZE_B])
{
if (same_literals(a, b))
return "";
return b;
}
enum class Tipos
{
Entero,
Cadena,
};
template<int SIZE_A, int SIZE_B, Tipos>
struct TiposTraits;
template<int SIZE_A, int SIZE_B>
struct TiposTraits<SIZE_A, SIZE_B, Tipos::Entero>
{
static auto Func(const char (&a)[SIZE_A], const char (&b)[SIZE_B])
{ return integral_value_or_zero(a,b); }
};
template<int SIZE_A, int SIZE_B>
struct TiposTraits<SIZE_A, SIZE_B, Tipos::Cadena>
{
static auto Func(const char (&a)[SIZE_A], const char (&b)[SIZE_B])
{ return string_or_empty(a,b); }
};
#define STRINGIFY(X) #X
#define INTEGRAL_VALUE_OR_ZERO(X) integral_value_or_zero(#X, STRINGIFY(X))
#define BOOL_VALUE_OR_FALSE(X) bool_value_or_false(#X, STRINGIFY(X))
#define STRING_OR_EMPTY(X) string_or_empty(#X, STRINGIFY(X))
#define NEW_PAIR(X,T)
std::make_pair(!same_literals(#X,STRINGIFY(X)),
TiposTraits<sizeof(#X),sizeof(STRINGIFY(X)),T>::Func(#X,STRINGIFY(X)))
#define _DEBUG FALSE
#define DRIVERS _09072007
enum stuff : int
{
cpp_version = INTEGRAL_VALUE_OR_ZERO(__cplusplus),
debug_enabled = INTEGRAL_VALUE_OR_ZERO(_DEBUG),
drivers_version = INTEGRAL_VALUE_OR_ZERO(DRIVERS),
test = INTEGRAL_VALUE_OR_ZERO(test),
};
namespace properties
{
const std::string cpp_version = STRING_OR_EMPTY(__cplusplus);
const std::string debug_enabled = STRING_OR_EMPTY(_DEBUG);
const std::string drivers_version = STRING_OR_EMPTY(DRIVERS);
const std::string test = STRING_OR_EMPTY(test);
auto const Cpp_version = NEW_PAIR(__cplusplus,Tipos::Cadena);
auto const Driver_version = NEW_PAIR(DRIVERS,Tipos::Entero);
auto const dummy = NEW_PAIR(dummy,Tipos::Cadena);
}
int main()
{
std::cout << "Cpp_version has value: " << std::get<0>(properties::Cpp_version) << '\n'
<< "Cpp_version value: " << std::get<1>(properties::Cpp_version) << '\n'
<< "Driver_version has value: " << std::get<0>(properties::Driver_version) << '\n'
<< "Driver_version value: " << std::get<1>(properties::Driver_version) << '\n'
<< "dummy has value: " << std::get<0>(properties::dummy) << '\n'
<< "dummy value: " << std::get<1>(properties::dummy) << '\n';
return 0;
}
The output generated by the program will now be as follows:Cpp_version has value: 1
Cpp_version value: 201406L
Driver_version has value: 1
Driver_version value: 9072007
dummy has value: 0
dummy value: