I need to write a spell book and I have two ways to do it - use enum, or use std :: map, as it is easier for me to use enum. But I ran into a problem how to display my enum?
I want to make it so that I can display all these spells on the screen and ask the user which of these spells do you want to use?
for example:
enum Book {
Tornado,
FireBall,
etc,
};
I want it to be output to the console like this :
choose one:
1.Tornado
2.FireBall
how to output this,for example with using array,is it possible?
If you want to display the enum (Tornado, FireBall) instead of 1, 2 you can create a separate function doing that display
#include <iostream>
enum Book {
Tornado,
FireBall,
};
void yourFunction(const Book& book)
{
switch(book)
{
case Book::Tornado:
std::cout<<"Tornado"<<std::endl;
break;
case Book::FireBall:
std::cout<<"FireBall"<<std::endl;
break;
default:
break;
}
}
int main()
{
Book b=Book::FireBall;
yourFunction(b);
return 0;
}
The general problem described here is associating a known integer value with a text string. The solution can be as straightforward as this:
enum Book {
Tornado,
Fireball,
last_index // see below
};
static const char* names[] = {
"Tornado",
"Fireball"
};
To display the menu, just go through the enumerators:
for (int i = Tornado; i < last_index; ++i)
std::cout << (i + 1) << '.' << names[i] << '\n';
You can do this because enumerators start at 0 and increase by 1, that is, the value of Tornado is 0 and the value of Fireball is 1. The value of last_index is 2.
The reason for using last_index is to make it easier to maintain the code. If you add an enumerator that loop doesn't change:
enum Book {
Tornado,
Fireball,
GladHands,
last_index // see below
};
static const char* names[] = {
"Tornado",
"Fireball",
"Glad Hands"
};
With the added enumerator, the value of GladHands is 2 and the value of last_index is 3, so the original loop still works.
Note that this does not generate text from the name of the enumerator. The name of the enumerator has restrictions on it that the text version doesn't, so you really can't generate text in most cases. In particular, GladHands has no spaces, but its text version has one.
I'm a beginner in C++ so I'm sorry if this is super obvious, but I have an issue with assigning values to an enum. I've declared the enum like so in a header file:
enum face
{ paramControlHeight = 40,
paramLabelWidth = 80,
paramSliderWidth = 300
};
And tried assigning an integer. Needless to say it doesn't work:
paramControlHeight = 40;//Not assignable
After googling about for a while, I tried:
using type_of_p=decltype(paramControlHeight);
Which as I understand, should yield the type of paramControlHeight, and enable me to use
paramControlHeight=static_cast<type_of_p> (40);
But I get the same "un-assignable" error
If anyone could point me in the right direction I'd be very grateful
I want to assign "paramControlHeight" which is inside my enum a different value. So, for example, it starts out as 40, but I would like to change it to 80 later on
You seem to misunderstand what enums are. You seem to expect the enum to behave like this
struct face
{ int paramControlHeight = 40;
int paramLabelWidth = 80;
int paramSliderWidth = 300;
};
face f; // create instance
f.paramControlHeight = 40; // modify member
However, an enum is rather like a
struct face
{
static const int paramControlHeight = 40;
static const int paramLabelWidth = 80;
static const int paramSliderWidth = 300;
};
Now back to your actual enum:
enum face
{ paramControlHeight = 40,
paramLabelWidth = 80,
paramSliderWidth = 300
};
Here paramControlHeight is an enumerator with the value 40. You cannot modify it. It is not meant to be modified. It is meant to enumerate. What you can do is:
face f{ paramControlHeight }; // create instance of face
f = paramSliderWidth; // assign a different value to it
A more typical enum would be
enum face_parts {
nose = 1,
eye = 2,
mouth = 3
};
That you could use like this
void print_face_part( face_parts fp ){
if (fp == nose) std::cout << "nose";
if (fp == eye) std::cout << "eye";
if (fp == mouth) std::cout << "mouth";
}
In simple terms an enum lets you name and group constants. Note that since C++11 there are scoped enums that are more flexible and dont introduce the name of the enumerators in the enclosing namespace.
paramControlHeight, paramLabelWidth, paramSliderWidth are the values. You can't assign anything to them more than you can assign a value to 42.
I am facing one issue regarding validating the size of array in C.
We have one .h file, which contains the constant value like
A 1
B 2
C 3
...
...
END 10
last element is END. Whenever any new constant is getting added, value of END should be increased. For e.g. if some added new constant call F which has value 10 then they have to increased the value of END from 10 to 11.
Now, in .c file we have one array of the size END. Each constant defined in .h file should have entry in this array, i.e.
abc[END] = {
1,
2,
1,
0,
...
...
}
Now, we have to add one test to check that, if anyone adding new entry in .h file and they have not added the corresponding entry in abc[] array, then test should fail. Earlier I have written the below piece of code to verify:
return (sizeof(tbl2schema)/sizeof(int) == END) ? TRUE : FALSE;
But I think, this code is not fulfilling the purpose. Hope, I have explained my issue.
Can anyone suggest me on this, How to do this check. I have to add a check that if anyone adding the entry in .h file then they have to add the entry in abc[] array.
The reason that your formula does not work is that you have explicitly specified END as the size. Remove it from the declaration to have the size of abc array change with the number of constants:
int abc[] = {
// ^^
1,
2,
1,
0,
...
...
};
If someone forgets to add a constant after updating END now, the size of abc is not going to match.
Use static_assert to check the condition:
static_assert(sizeof(tbl2schema)/sizeof(int) == END, "abc size does not match.");
It seems the perfect usage for enum :
file.h :
typedef enum { A, B, C, END } constants_list;
file.c :
static int abc[END] = { 1, 2, 1 };
Your table abc will grow automatically as the list of constants grow.
Note however that any value in the table will be initialized to 0 if not explicitly set in file.c.
Maybe you want:
return (sizeof(tbl2schema) / sizeof(int) > END) ? TRUE : FALSE;
which is equivalent of:
return sizeof(tbl2schema) / sizeof(int) > END;
Below is a C solution that does not compile if there is a discrepency between the header and the source file:
Header
enum {
NAME_1,
NAME_2,
NAME_3,
END
};
#define STATIC_ASSERT_ARRAY_SIZE(array, expected_size) \
static int static_assert_array_size_array_##array##_too_small \
[sizeof(array)/sizeof(array[0]) - expected_size]; \
static int static_assert_array_size_array_##array##_too_large \
[expected_size - sizeof(array)/sizeof(array[0])]
Source #1
#include "header.h"
int data[] = { 5, 8 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
return 0;
}
Source #2
#include "header.h"
int data[] = { 5, 8, 10 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
return 0;
}
Source #3
#include "header.h"
int data[] = { 5, 8, 10, 11 };
STATIC_ASSERT_ARRAY_SIZE(data, END);
int main(void)
{
return 0;
}
Compilation
$ gcc main1.c
main.c:3: error: size of array static_assert_array_size_array_data_too_small is too large
$
$ gcc main2.c
$
$ gcc main3.c
main.c:3: error: size of array static_assert_array_size_array_data_too_large is too large
$
This trick takes advantage from the fact that it is illegal to declare an array of negative size (you don't say !).
I'm assuming you've simplified the example a bit, but it looks like you just have a big array of values that you want to be able to access by name rather than with a "magic number" index—hence the #defines.
If that's the case, why aren't you just using a struct instead, with sequential fields named the same as your current symbolic names for the array indices?
struct { int A, B, C, ... } abc = {
1,
2,
3,
...
};
This way, the compiler will check to make sure you're not accessing an invalid name. You can still iterate over the struct members using pointers:
for (int *p = &abc.A; p <= &abc.Z; abc++) {
do_something_with(*abc);
}
I would like to recommend a more general solution for the problem of interdependent, but scattered data in a program.
It's using a single preprocessor macro file which contains all information in a single place, in the form of a list of preprocessor function macros. The file is included wherever the information is needed to define data structures or types like enums.
In this example I use a list of paper size defintions; this served me nicely in real life when I wrote a PCL6 parser. In PCL6 each paper size is indeed a numerical token. Paper sizes have quite a few associated attributes, as well as a human readable name. Frequently the need occurs to map bidirectionally between name and token value and look up associated information. This leads to several data structures with redundant information plus a matching enum definition. It's easy to miss an update in one of them when adding a new paper type (in reality there are many dozen).
The trick is to define an entry in the macro file to a language construct which is suitable in the given place. Note how the language was carefully designed to allow trailing commas e.g. at the end of an enum definition or initializer lists. This is the use case for that.
We start with the file containing the macros which hold the information associated with a paper size. In reality there are of course many more, and more attributes.
//////////////////////////////////////////////////////
// papermacros.h
//////////////////////////////////////////////////////
// Has all information about paper sizes in one place.
// Note the function syntax. It's essential.
// token, name, height, width
PAPERSIZE_MACRO(5, A5, 200, 150)
PAPERSIZE_MACRO(4, A4, 300, 200)
PAPERSIZE_MACRO(3, A3, 400, 300)
Then the paper class and enum. The macros are used to build an enum type
of paper tokens which always contains all entries in the
macro file. The enum element names are constructed with the preprocessor concatenation operator, names are constructed using the stringize operator. (We cannot have strings in the macro header right away because we want to use the name also as a base for the enum identifier -- there is no "unstringize" operator.)
//////////////////////////////////////////////////////
// papers.h
//////////////////////////////////////////////////////
#include <string>
#include <map>
#include <sstream>
#undef PAPERSIZE_MACRO
#define PAPERSIZE_MACRO(token, name, height, width) \
e_##name = token,
enum PaperSizeE {
e_INVALID, // for default ctor
# undef PAPERSIZE_MACRO
# define PAPERSIZE_MACRO(token, name, height, width) \
e_##name = token,
// this included file expands to a series of enum definitions which
// make sure that each enum element is named
// like the paper name, with a prefix e_
# include "papermacros.h"
e_END // if you want. Note, however, that it has the (arbitrary)
// value of the last "real" enum plus 1.
#undef PAPERSIZE_MACRO
};
class PaperT
{
public:
PaperSizeE token;
int height;
int width;
std::string name;
PaperT(PaperSizeE t, std::string n, int h, int w)
:token(t), name(n), height(h), width(w)
{ }
// Funny, needed by map resp. std::pair
PaperT() : token(e_INVALID), name("invalid"), height(0), width(0)
{}
std::string ToString()
{
std::ostringstream stm;
stm << name << ", height: " << height << ", width: " << width;
return stm.str();
}
};
// Useful mappings. Paper information can now be
// efficiently looked up by token or by name.
extern std::map<PaperSizeE, PaperT> mapByToken;
extern std::map<std::string, PaperT> mapByName;
The next file contains the definitions for the maps declared above. Again the elements of the initiailzer list are constructed from the (multiple times) included macro header, with the respective suitable macro definitions. Here, too, trailing commas are ignored.
//////////////////////////////////////////////////////////////////
// paperdefs.cpp
//////////////////////////////////////////////////////////////////
#include "papers.h"
using namespace std;
std::map<PaperSizeE, PaperT> mapByToken
{
# define PAPERSIZE_MACRO(token, name, height, width) \
{e_##name, PaperT(e_##name, #name, height, width) },
// this expands to a series of
// { e_xx, PaperT(e_xx, "Ax", hhh, www) },
// which is used to initialize the entries a map enum -> paper.
# include "papermacros.h"
# undef PAPERSIZE_MACRO
};
std::map<string, PaperT> mapByName =
{
# define PAPERSIZE_MACRO(token, name, height, width) \
{#name, PaperT(e_##name, #name, height, width) },
// this expands to a series of
// { "Ax", PaperT(e_xx, "Ax", hhh, www) },
// which is used to initialize the entries a map name -> paper.
# include "papermacros.h"
# undef PAPERSIZE_MACRO
};
Last not least a main function in order to demonstrate usage.
//////////////////////////////////////////////////////////////////
// main.cpp
// Demonstrate how to use the paper related data structures.
// Must be linked with paperdefs.o
//////////////////////////////////////////////////////////////////
#include "papers.h"
#include <iostream>
using namespace std;
int main()
{
{
PaperSizeE a4Token = e_A4;
cout << "The name of the paper with token " << a4Token
<< " is " << mapByToken[a4Token].name << endl;
}
{
string name = "A3";
cout << "The token val of the paper named " << name
<< " is " << mapByName[name].token << endl;
}
// iterate over all papers
for(auto &p: mapByName)
{
cout << "Paper by name " << p.first << ": "
<< p.second.ToString() << endl;
}
}
The result:
$ g++ -std=c++11 -o papers main.cpp paperdefs.cpp && ./papers
The name of the paper with token 4 is A4
The token val of the paper named A3 is 3
Paper by name A3: A3, height: 400, width: 300
Paper by name A4: A4, height: 300, width: 200
Paper by name A5: A5, height: 200, width: 150
Weird question and hard to word but lets say I have a 2 files that have a string of what double variables will appear in the file at the top and then the corresponding double variables, something like:
File1 =
A B C D E
1.2 3.4 4.5 5.6 7.8
File2=
B D E
9.8 7.6 5.4
and I have a struct of doubles
struct numb{
double A,B,C,D,E};
is it possible to read in the string in file 1 (A B C D E) and whatever the first value in the string is (A) assign it to the corresponding struct value numb.A.
So then the next file it will read in the first value of the string (B) and assign it to numb.B.
I realize this is possible with a bunch of if statements but I was wondering if there is an easier way. The hardest part is the string of variables will always be some combination of A,B,C,D,E. I am programming in C++ VS10
You can create a map with the string to parse as the key, and a pointer to member of the corresponding attribute of your structure as the value.
std::map<std::string, double numb::*> mapLetterToCorrespondingAttribute;
Then parse your file and assign the value to the corresponding member pointed to by the value in your map corresponding to the key being the letter you parsed.
Read this multiple times before you say you don't understand :D
A switch is probably the easiest way to do this.
void set_member(numb &n, char member, double value)
{
switch (member) {
case 'A':
n.A = value;
break;
case 'B':
n.B = value;
break;
// etc.
default:
// handle error
}
}
Declare an array of double in struct numb.
struct numb {
void setValue(char label, double val) { value[label-'A'] = val; }
double getValue(char label) const { return value[label-'A']; }
double value[5];
};
Then, you could perform:
numb n;
n.setValue('A', 1.2);
double A = n.getValue('A');
Read the two lines into std::vector<std::string> and then put them into a map in pairs:
std::vector<std::string> vars; // the split up first line
std::vector<std::string> values; // split up second line
std::map<std::string, double> mapping;
for (int i = 0; i < vars.size(); ++i) {
mapping.insert(std::make_pair(vars[i], boost::lexical_cast<double>(values[i]));
}
If you pre-populate the map mapping with sensible default values, this should be quite simple. Also, you can substitute the call to boost::lexical_cast<double> with any conversion method you like.
Suppose I am working on a card game, and I am using the numbers 0 to 3 to represent the suits internally, as it's easier to work with numbers. So:
0 is equivalent to hearts
1 is equivalent to clubs
2 is equivalent to spades
3 is equivalent to diamonds
When I need to output the suits as strings, though, I can easily use an array of strings to convert them, like this one:
char *suits[] = {"heats","clubs","spades","diamonds"};
So that I can type:
cout << suits[card.suit]
and the output would be the exact string of the suit.
What if I want to do this the other way around though? That is, I'll be reading the suits from a file as strings, and I want to convert them to their respective numerical value (0 to 3) on the fly. How can I do it?
My initial idea was to create a very small hash table (i.e., 4 elements in this case), then hash the strings as I read them and get their respective numerical value from the hash table.
Is there an easier way I am missing (specifically in C or C++)?
std::map<std::string, int> assoc;
assoc["hears"] = 0;
assoc["clubs"] = 1;
...
char *suits[] = {"heats","clubs","spades","diamonds"};
for (char *data : suits)
{
std::cout << assoc[data];
}
Like Joachim said, I would recommend a std::map<std::string, int>.
You can then do stuff like.
std::cout << map["heart"];
I would recommend to check out the std::map class as it is quite a nice tool, but also holds some gotchas.
If you want to use it in both directions, you could also use a boost::bimap.
std::map<std::string, int id> cardsToIdMap;
int stringToCardId(std::string s) {
return cardsToIdMap[s];
}
A map is hugely overkill here:
#define SIZE(x) (sizeof (x)/sizeof(*(x)))
const char *suits[] = {"heats","clubs","spades","diamonds"};
int suit_to_int(char *s)
{
for(int x=0; x<SIZE(suits);x++)
if(strcmp(s, suits[x])==0)
return x;
return SUIT_ERR;
}
char* suit;
if (*suit == 'h') {
return 0;
} else if ...