enum class Fruit { apple, orange, pear };
enum class Color { red, green, orange };
template <typename T> struct Traits;
//I have to return the appropriate value(string) of color and fruit in their respective structs.
//I could do this by switch case method but I specifically wanted to know, how do I access an enum class through index
template<>
struct Traits<Fruit>{
static string name(int i){
if(i>-1&&i<3){
return Fruit::static_cast<Fruit>(i);
}
else{
return "unknown";
}
}
};
template<>
struct Traits<Color>{
static string name(int i){
if(i>-1&&i<3){
return Color::static_cast<Color>(i);
}
else{
return "unknown";
}
}
};
I want to return the appropriate string present in the respective structs at their respective indices.
The static_cast is not working and compiler is giving an error that it can't cast. I wonder if it is possible to access enum class through index at all.
Error:
could not convert ‘(Fruit)i’ from ‘Fruit’ to
‘std::__cxx11::string {aka std::__cxx11::basic_string<char>}’
return static_cast<Fruit>(i);
As error says you cannot directly convert enum class into string. But you can convert int into enum class using static_cast
After converting int into enum you can go ahead with enum to string conversion. There are many ways to do and this so answer has good summary of that
static string name(int i) {
if (i>-1 && i<3) {
Fruit f = static_cast<Fruit>(i);
return name(f);
}
else {
// return default value or throw error
return "";
}
}
static string name(Fruit f) {
//convert f and return string
}
You could have an array of fruit names in parallel to your enum to have names:
std::string FruitNames[] = { "apple", "orange", "pear" };
Now you can use this directly to get names from. If you are willing to invest a little into x-macros, you can generate both your array and the enum from with one single macro:
#define FRUITS GEN_FRUIT(apple) GEN_FRUIT(orange) GEN_FRUIT(pear)
#define GEN_FRUIT(X) X,
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#define GEN_FRUIT(X) [Fruit::X] = #X,
std::string const FruitNames[] = { FRUITS };
#undef GEN_FRUIT
Admitted, harder to read, on the other hand, you have just one single location to maintain both enum and array...
You could even support explicit values:
#define FRUITS GEN_FRUIT_V(apple, 1) GEN_FRUIT(orange) GEN_FRUIT_V(pear, 4)
#define GEN_FRUIT(X) X,
#define GEN_FRUIT_V(X, Y) X = (Y),
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V
#define GEN_FRUIT(X) GEN_FRUIT_V(X, Fruit::X)
#define GEN_FRUIT_V(X, Y) std::make_pair(static_cast<Fruit>(Y), std::string(#X)),
std::unordered_map<Fruit, std::string> const FruitNames({ FRUITS });
// or (tree) map, if you prefer
//std::map<Fruit, std::string> const FruitNames({ FRUITS });
#undef GEN_FRUIT
#undef GEN_FRUIT_V
With upcoming C++20 (or a compiler supporting designated initialisers as extension), you could use an array again:
#define GEN_FRUIT(X) X,
#define GEN_FRUIT_V(X, Y) X = (Y),
enum class Fruit { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V
#define GEN_FRUIT(X) [static_cast<int>(Fruit::X)] = #X,
#define GEN_FRUIT_V(X, Y) GEN_FRUIT(X)
std::string const FruitNames[] = { FRUITS };
#undef GEN_FRUIT
#undef GEN_FRUIT_V
Yet future music... Result in above example would be an enum with gaps and the array filled with empty strings at the gaps. If gaps are large (or negative enum values exist), this approach would result in large array as well, so rather not appropriate any more, you might prefer falling back to the map solution again...
Related
Assume I have a macro that makes a class member in c++. Is there a way to modify the below example to automatically create NumbersILike::nums? Assume the objects created aren't the same sizeof.
#define MAKE_NUM(num) int num
class NumbersILike
{
MAKE_NUM(three);
MAKE_NUM(four);
MAKE_NUM(five);
std::vector<int*> nums = { &three,&four,&five };
};
It's possible. I looked up 'X Macro'.
class NumbersILike
{
public:
#define LIST_OF_ITEMS \
X(three) \
X(four) \
X(five)
#define X(nm) int nm;
LIST_OF_ITEMS
#undef X
std::vector<int*> derp = {
#define X(nm) &nm,
LIST_OF_ITEMS
#undef X
};
};
I've been developing a text-based adventure game, in which I want to store individual items as instances of a few structs: Item, Armor, and Weapon. Problem is, even though my Item struct works, whenever I compile I am getting Armor does not name a type and Weapon does not name a type errors on a couple of const NoArmor/Weapon placeholders. I have checked through other threads on this topic, but none of them have proved to be applicable or useful.
Here is the code I am referring to:
#ifndef ITEMS_H_
#define ITEMS_H_
#include <string>
#include "Types.h"
using namespace std;
struct Item {
string name;
double weight;
double value;
ItemType type;
};
struct Armor {
Item base;
double armorMod;
WeaponType weakness;
WeaponType resistance;
ArmorType armorType;
};
struct Weapon {
Item base;
double damageMod;
double agilityMod;
WeaponType weaponType;
};
const Item NoItem = {"_NO_ITEM_", 0, 0, NoItemType};
const Armor NoArmor = {NoItem, 0, NoWeaponType, NoWeaponType, NoArmorType};
const Weapon NoWeapon = {NoItem, 0, 0, NoWeaponType};
#endif /* ITEMS_H_ */
EDIT: Types.h is causing the issue
Types.h:
#ifndef TYPES_H_
#define TYPES_H_
enum ItemType {
NoItemType,
Food,
Misc,
Weapon,
Armor
};
enum ArmorType {
NoArmorType,
Helmet,
Tunic,
Chestplate,
Bracers,
Gloves,
Gauntlets,
Pants,
Greaves,
Robes,
Sabatons,
Boots
};
enum WeaponType {
NoWeaponType,
Sword,
Dagger,
Hammer,
Axe,
Staff,
Bow
};
enum ClassType {
NoClassType,
Knight,
Warrior,
Thief,
Blacksmith,
Lumberjack,
Mage,
Archer
};
enum RaceType {
NoRaceType,
Human,
WoodElf,
Orc,
DarkElf,
Dragonling,
Dwarf
};
enum SpecialtyType {
NoSpecialtyType,
TwoHand,
OneHand,
Archery,
Necromancy,
Conjuration,
Destruction
};
#endif /* TYPES_H_ */
Solved: Issue was the ItemValue enum containing identical values (Weapon and Armor).
I have the following situation: Depending on some parameter that my function takes it have to create different types:
I want to do something like this:
if(variant==1){
#define my_type int;
}
else{
#define my_type double;
}
cout<<sizeof(my_type);
and then use my_type in my further code.
So, that in case of variant=1 sizeof(my_type) gives 4 and for variant=2 it gives 8.
How can this be done? Either in this manner or another.
Thanks.
I agree with #Magnus Hoff in that what you asked cannot be done. But there are two approximations.
Option 1: make variant a macro.
#ifdef variant
# define my_type int
#else
# define my_type double
#endif
Option 2: use template function.
Instead of
void func(int variant) {
if (variant==1)
#define my_type int
else
#define my_type double
my_type ...
}
do this:
template<typename my_type> void func() {
my_type ...
}
Replace this:
if(variant==1){
#define my_type int;
}
else{
#define my_type double;
}
cout<<sizeof(my_type);
… with this:
template< class Type >
void foo()
{
// ...
cout<<sizeof(Type);
}
// ...
if( variant==1 )
{
foo<int>();
}
else
{
foo<double>();
}
Note that a runtime value can't affect compile time decisions. Without a time travel device.
I have a class like this:
class TType {
public:
...
enum binary_type {
bt_a = 0,
bt_xyz,
....
bt_ak = 10,
....
};
}
and I use it in several places, also the enum:
if(var12 == TType::bt_a ) { ....
Now I imported a C library which has exactly the same enum (same keys, same values, same size) inside one of it's headerfiles:
typedef enum data_types_e {
bt_a = 0,
bt_xyz,
....
} data_types;
How can I define the enum in the c++ class definition to use the declaration of the c headerfile?
I want to continue using the enum the same way as before (TType::bt_a), and avoid copying the whole enum. Furthermore I don't wont to modify the library (otherwise a preprocessor-macro would do the trick) and I want changes made in the library also be made to the enum in my class.
Neither a typedef inside the c++ class definition nor a type alias (c++11) seem to work in this situation.
"How can I define the enum in the c++ class definition to use the declaration of the c headerfile?"
You can simply reuse the values from the c-style enum:
#include "TheOtherEnum.h"
...
enum binary_type {
bt_a = ::bt_a,
bt_xyz = ::bt_xyz,
....
bt_ak = ::bt_ak,
....
};
"Neither a typedef inside the c++ class definition nor a type alias (c++11) seem to work in this situation."
Yes these would work to provide the correct enum type, but you'll still need to qualify the values from the global namespace and not for nested to your class.
You can make C++ enum dependant of C enum:
typedef enum data_types_e {
bt_a = 0,
bt_xyz,
....
} data_types;
// ...
class TType {
public:
...
enum binary_type {
bt_a = bt_a,
bt_xyz = bt_xyz,
....
};
}
If possible, try renaming your class TType say, class TType_1.
//=============CLibraryFileContainingEnum.h=================
typedef enum data_types_e
{
bt_a = 9999,
bt_xyz
} data_types;
//==========================================================
//==========================================================
class TType_1
{
public:
enum binary_type
{
bt_a = 8878,
bt_xyz
};
};
namespace TType
{
#include "CLibraryFileContainingEnum.h"
}
int main()
{
int a = TType::bt_a; //this prints 9999
cout << a << endl;
return 0;
}
//==========================================================
Here is my answer, this is an old topic, but better lately than never
class TType {
public :
public:
...
#undef __cplusplus
#include "yourheaderc.h"
#define __cplusplus
}
maybe with a typedef binary_type data_types to preserve your nominations
But this solution is available if your header don't contains syntax C forbidden in C++. I'm currently searching a new solution because there are prototypes in my header that contains something like :
void afunction( unsigned long id,
enum T_TYPE type,
enum T_CHAR characteristic,
unsigned short number,
unsigned short value);
because T_TYPE and T_CHAR are not typedef-ed but this syntax is non-sense in C++ because it's the declaration syntax. So my solution is not appropriate if you are in a similar case.
I define an enums using include,since there are different enums which have the same enumeration data and I want to reuse it:
#define X(SYM) SYM
#define X_INIT(SYM,VAL) SYM = VAL
/// Destination for scalar memory read instruction
enum SSRC
{
#include "GenericInstructionFields1.h"
#include "ScalarInstructionFields.h"
#include "GenericInstructionFields2.h"
};
enum SDST
{
#include "GenericInstructionFields1.h"
};
#undef X_INIT
#undef X
};
But I can`t compile the code for SDST. The compiler writes redefinition for a fields of SSRC,which comes from "GenericInstructionFields1.h". What is the cause of the problem and how can it be solved?
//GenericInstructionFields1.h
/// SGPR0 to SGPR103: Scalar general-purpose registers.
X_INIT(ScalarGPRMin,0),
X(ScalarGPR),
X_INIT(ScalarGPRMax,103),
/// 104 – 105 reserved.
X(Reserved104),
X(Reserved105),
X_INIT(Reserved,2),
/// vcc[31:0].
X_INIT(VccLo, 106),
/// vcc[63:32].
X(VccHi),
You can't have enumerations with the same enumerators in the same namespace. This would reproduce your problem:
enum X {A,B};
enum Y {A};
either use namespaces or prefix your enum values.
Enums are not like namespaces.
You will see the same error with the following
enum A
{
P, Q
};
enum B
{
P, Q
};
You can achieve what you want by this
struct A
{
enum { P, Q };
};
struct B
{
enum { P, Q };
};
You can now use A::P, A::Q, B::P & B::Q
Or in your case
#define X(SYM) SYM
#define X_INIT(SYM,VAL) SYM = VAL
/// Destination for scalar memory read instruction
struct SSRC
{
enum
{
#include "GenericInstructionFields1.h"
#include "ScalarInstructionFields.h"
#include "GenericInstructionFields2.h"
}
};
struct SDST
{
enum
{
#include "GenericInstructionFields1.h"
}
};
#undef X_INIT
#undef X
};
You can now use SSRC::ScalarGPRMax and SDST::ScalarGPRMax