I have a struct called CoolStruct:
struct CoolStruct
{
int id;
uint32 type;
uint32 subtype;
String name;
};
I have a vector of these structs as well:
std::vector<CoolStruct> coolVector;
I want to create a bunch of structs which have predefined values to push_back into this coolVector. I'd like to keep the code from getting cludgy and ugly. I would really like to keep this notation:
CoolStruct t = {1, EQData::EQ_EFFECT_TYPE_PARAMETRIC, 0, T("Parametric")};
coolVector.push_back(t);
CoolStruct t = {2, EQData::EQ_EFFECT_TYPE_FILTER_LOW_PASS,EQData::EQ_FILTER_TYPE_FILTER_BUTTERWORTH_12DB, T("Low Pass")};
coolVector.push_back(t);
But of course this doesn't work... Not allowed to do a reinitialization. Is there any other solution to make this as readable as possible? The only alternative I can think of is it manually set each paramater of the struct:
t.id = whatever; t.type = somethingelse; t.subtype = thisisalotofcode; t.name = justtosetupthisvector;
coolVector.push_back(t);
how about:
CoolStruct t1 = {1, EQData::EQ_EFFECT_TYPE_PARAMETRIC, 0, T("Parametric")};
coolVector.push_back(t1);
CoolStruct t2 = {2, EQData::EQ_EFFECT_TYPE_FILTER_LOW_PASS,EQData::EQ_FILTER_TYPE_FILTER_BUTTERWORTH_12DB, T("Low Pass")};
coolVector.push_back(t2);
In C++0x, I think you should be able to do:
CoolStruct t;
t = {1, EQData::EQ_EFFECT_TYPE_PARAMETRIC, 0, T("Parametric")};
coolVector.push_back(t);
t = {2, EQData::EQ_EFFECT_TYPE_FILTER_LOW_PASS,EQData::EQ_FILTER_TYPE_FILTER_BUTTERWORTH_12DB, T("Low Pass")};
coolVector.push_back(t);
or even:
coolVector.push_back({1, EQData::EQ_EFFECT_TYPE_PARAMETRIC, 0, T("Parametric")});
coolVector.push_back({2, EQData::EQ_EFFECT_TYPE_FILTER_LOW_PASS,EQData::EQ_FILTER_TYPE_FILTER_BUTTERWORTH_12DB, T("Low Pass")});
In fact, if you really want to get creative (and you don't have any previous elements that you want to keep), you can replace the whole vector with this syntax:
coolVector = {
{1, EQData::EQ_EFFECT_TYPE_PARAMETRIC, 0, T("Parametric")},
{2, EQData::EQ_EFFECT_TYPE_FILTER_LOW_PASS,EQData::EQ_FILTER_TYPE_FILTER_BUTTERWORTH_12DB, T("Low Pass")}
};
if you add a simple constructor:
struct CoolStruct
{
CoolStruct(int id, uint32 type, uint32 subtype, String name) : id(id), type(type), subtype(subtype), name(name) {}
int id;
uint32 type;
uint32 subtype;
String name;
};
you can then do this:
CoolVector.push_back(CoolStruct(1, EQData::EQ_EFFECT_TYPE_PARAMETRIC, 0, T("Parametric")));
CoolVector.push_back(CoolStruct(2, EQData::EQ_EFFECT_TYPE_FILTER_LOW_PASS,EQData::EQ_FILTER_TYPE_FILTER_BUTTERWORTH_12DB, T("Low Pass")));
Related
I am creating multiple different types of encoders where the main difference is the different data structures used to initialize the class. My header is something like this
struct tagTypeInfo {
uint16_t start;
uint16_t last;
uint16_t count;
std::string name;
rdwrT rdwr;
};
template <typename T>
class encodedTag
{
public:
encodedTag(vector<tagTypeInfo> tagInfo_) : tagInfo(tagInfo_)
{
int start = 0;
for(auto & tag : tagInfo)
{
tag.start = start;
tag.last = start + tag.count - 1;
start = start + tag.count;
}
}
uint16_t encode(uint16_t tag, T tagType)
{
assert(tag<tagInfo[tagType].count)
return( tagInfo[tagType].start + tag );
}
std::tuple<uint16_t, T> decode(uint16_t encodedTag)
{
int type = 0;
uint16_t tag;
// simple linear search as there are only a few entries
for (auto it = begin(tagInfo); it != end(tagInfo); it++)
{
if (encodedTag >= it->start && encodedTag < it->last )
{
// tag is in the range
return {encodedTag - it->start , (T)type};
}
type++;
}
assert(false);
return {0,(T)0};
}
std::string getName(T tagType) {return(tagInfo[tagType].name);}
rdwrT getRdwr(T tagType) {return(tagInfo[tagType].rdwr);}
private:
std::vector<tagTypeInfo> tagInfo;
};
extern std::vector<tagTypeInfo> rdTag;
extern std::vector<tagTypeInfo> wrTag;
//using rdTagEncode = encodedTag<rdTagT>(rdTag) <-- error
The cpp file contains:
std::vector<tagTypeInfo> rdTag {
{0, 0, NUM_HOSTRDTAG, "HostRdTag", RDWR_RD},
{0, 0, NUM_SYSRDTAG, "SysRdTag", RDWR_RD},
{0, 0, NUM_GCRDTAG, "GCRdTag", RDWR_RD}
};
std::vector<tagTypeInfo> wrTag {
{0, 0, NUM_HOSTWRTAG, "HostWrTag", RDWR_WR},
{0, 0, NUM_SYSWRTAG, "SysWrTag", RDWR_WR},
{0, 0, NUM_GCWRTAG, "GCWrTag", RDWR_WR}
};
My goal is to be able to just declare an encoder in the code elsewhere with
rdTagEncode myEncode;
However I cant seem to figure out the right syntax to do this. Any suggestions?
Using a derived class was the best solution. Thanks for the suggestion #appleapple
class encodedRdTag : public encodedTag<rdTagTypeT>
{
public:
encodedRdTag() : encodedTag({
{0, 0, NUM_HOSTRDTAG, "HostRdTag", RDWR_RD},
{0, 0, NUM_SYSRDTAG, "SysRdTag", RDWR_RD},
{0, 0, NUM_GCRDTAG, "GCRdTag", RDWR_RD}
}) {};
};
class encodedWrTag : public encodedTag<wrTagTypeT>
{
public:
encodedWrTag() : encodedTag({
{0, 0, NUM_HOSTRDTAG, "HostRdTag", RDWR_RD},
{0, 0, NUM_SYSRDTAG, "SysRdTag", RDWR_RD},
{0, 0, NUM_GCRDTAG, "GCRdTag", RDWR_RD}
}) {};
};
For completeness, here is the "dispatch based on type" method which I mentioned in comment above.
NOTE: this code doesn't work in original question as the rdTag and wrTag are same type thus independent of the class template T, but according to your own answer this may be actually what happends.
#include <vector>
struct tagTypeInfo{};
struct rdTagTypeT{};
struct wrTagTypeT{};
std::vector<tagTypeInfo> get_default_info(rdTagTypeT); // return wrTag
std::vector<tagTypeInfo> get_default_info(wrTagTypeT); // return wdTag
template <typename T>
struct encodedTag
{
encodedTag():encodedTag(get_default_info(T{})){}
encodedTag(std::vector<tagTypeInfo> tagInfo) : tagInfo(tagInfo){};
std::vector<tagTypeInfo> tagInfo;
};
using encodedRdTag = encodedTag<rdTagTypeT>;
using encodedWrTag = encodedTag<wrTagTypeT>;
void foo(){
encodedRdTag rd;
encodedWrTag rw;
}
For context I am recreating a Pokemon game in c++(I am new to C++ i have only 1 year xp in programming and i was programming in C). But i have issue with the organisation of my code.
Here is my problem: I don't know what to put in class and what to put in struct.
For example in my current code my Pokemon and my Attaque are 2 different class. I have done that because of the fact that I want the 2 of them to be init with info in a file. But if the Pokemon are fighting they need to have access to the Attaque. But when generating my Pokemon some stat change when it's affected by item. Like shiny % is affected by Shiny Charm. So i need to have access to my bag too.
The problem here is that I am making a lot of class friend (right now Attaque is friend of Pokemon and Pokemon is friend of bag). And I have read that it's maybe an organisation code issue.
I have thought about making getters for example for the Shiny Charm, but I don't know if it's good or not.
Here is my Pokemon class. nature_e, status_e and type_e are enum class.
class Pokemon {
private:
bool m_is_shiny { false };
bool m_is_pokerus { false };
short m_level { 0 };
int m_id { 0 };
int m_exp { 0 };
std::array<int, 2> m_texture_coord { 0, 0 };
std::array<unsigned char, 6> m_iv {0, 0, 0, 0, 0, 0};
std::array<unsigned char, 6> m_ev {0, 0, 0, 0, 0, 0};
std::array<unsigned char, 6> m_base_stat {0, 0, 0, 0, 0, 0};
std::array<unsigned char, 8> m_stat {0, 0, 0, 0, 0, 0, 0, 0};
nature_e m_nature {nature_e::NONE};
status_e m_status {status_e::NONE};
std::array<type_e, 2> m_type {type_e::NONE, type_e:: NONE};
Texture2D m_texture;
std::string m_name { "Non" };
std::string m_item { "non" };
std::string m_sprite_path { "None" };
std::array<Attaque, 4> m_atk { Attaque(), Attaque(), Attaque(), Attaque() };
public:
Pokemon()
{
}
Pokemon(int id, short level);
};
Here is my Attaque class:
class Attaque {
private:
bool m_phy_or_spe;
int m_damage;
type_e m_type;
double m_effect_precision;
double m_precision;
std::string m_name;
public:
Attaque()
{
}
Attaque(int id);
};
Is there a way to construct a boost::bimap (or multi-index container) that has two keys, plus a value they both point to? In addition, you can query one key to get the other?
I can construct a boost multi-index container that has two keys for some element, but cannot figure out how to get the value of a key given the value of the other key?
I am trying to do something like:
struct s {};
int main()
{
typedef std::map<int, std::shared_ptr<s>> left_map_type;
typedef std::map<std::string, std::shared_ptr<s>> right_map_type;
typedef boost::bimap<left_map_type, right_map_type> bi_map_type;
typedef bi_map_type::value_type value_type;
bi_map_type bim;
std::shared_ptr<s> sp = std::make_shared<s>();
std::shared_ptr<s> sp2 = std::make_shared<s>();
left_map_type lm { {1, sp}, {2, sp2} };
right_map_type rm { {"foo", sp}, {"bar", sp2} };
bim.insert(lm, rm);
/*
For instance, given the key "foo", search bim to obtain BOTH the shared pointer sp as well as the alternate key '1'.
*/
}
I would use a multi-index container over records like:
struct Data { }; // your "s"
struct Record {
int id;
std::string name;
Data data; // std::shared_ptr<Data>?
};
Now you could make a container that adds unique indexes by id and name:
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
bmi::ordered_unique< bmi::tag<struct by_id>, bmi::member<Record, int, &Record::id>>,
bmi::ordered_unique< bmi::tag<struct by_name>, bmi::member<Record, std::string, &Record::name>>
> >;
Formatting more verbosely:
using Table = boost::multi_index_container<
Record,
bmi::indexed_by<
bmi::ordered_unique<
bmi::tag<struct by_id>,
bmi::member<Record, int, &Record::id>>,
bmi::ordered_unique<
bmi::tag<struct by_name>,
bmi::member<Record, std::string, &Record::name>>>>;
See below for a less verbose way;
Now you can make your table and access it using any of the indices:
Table table;
auto& left = table.get<by_id>(); // or get<0>
auto& right = table.get<by_name>(); // or get<1>
Whatever interface you use, any changes will reflect in all other indexes, and uniqueness constraints are guaranteed. E.g.
table.emplace(1, "one", Data{"Sleepy", {1, 2, 3}});
table.emplace(2, "two", Data{"Grumpy", {2, 4, 6}});
table.emplace(3, "three", Data{"Sneezy", {3, 6, 9}});
Just printing them (using libfmt for demo):
// Simple enumeration:
fmt::print("Just the table:\n - {}\n", fmt::join(table, "\n - "));
fmt::print("By id:\n - {}\n", fmt::join(left, "\n - "));
fmt::print("By name:\n - {}\n", fmt::join(right, "\n - "));
Prints
Just the table:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
By id:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
By name:
- Record{1, one, Data{Sleepy, {1, 2, 3}}}
- Record{3, three, Data{Sneezy, {3, 6, 9}}}
- Record{2, two, Data{Grumpy, {2, 4, 6}}}
This exemplifies that the default index is the first index declared (the "left view" here, or as I'd prefer to call it: by_id).
Searching is just like you'd expect from a standard container:
auto id2 = left.find(2);
auto nameTwo = right.find("two");
if (id2 != left.end())
fmt::print("id2: {}\n", *id2);
if (nameTwo != right.end())
fmt::print("nameTwo: {}\n", *nameTwo);
For non-unique indexes, equal_range is useful. There's lower_bound and upper_bound etc.
Live Demo
Live On Compiler Explorer
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>
#include <memory>
struct Data {
std::string extra;
std::vector<int> ints;
};
struct Record {
int id;
std::string name;
Data data; // std::shared_ptr<Data>?
};
namespace bmi = boost::multi_index;
#define Index(name) \
bmi::ordered_unique< \
bmi::tag<struct by_##name>, \
bmi::member<Record, decltype(Record::name), &Record::name>>
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
Index(id),
Index(name)
> >;
#include <fmt/ranges.h>
template <>
struct fmt::formatter<Data, char> : fmt::formatter<std::string, char> {
auto format(Data const& data, auto& ctx) {
return fmt::format_to(ctx.out(), "Data{{{}, {}}}", data.extra,
data.ints);
}
};
template <>
struct fmt::formatter<Record, char> : fmt::formatter<std::string, char> {
auto format(Record const& rec, auto& ctx) {
return fmt::format_to(ctx.out(), "Record{{{}, {}, {}}}", rec.id,
rec.name, rec.data);
}
};
int main()
{
Table table;
auto& left = table.get<by_id>(); // or get<0>
auto& right = table.get<by_name>(); // or get<1>
table.emplace(1, "one", Data{"Sleepy", {1, 2, 3}});
table.emplace(2, "two", Data{"Grumpy", {2, 4, 6}});
table.emplace(3, "three", Data{"Sneezy", {3, 6, 9}});
// Simple enumeration:
fmt::print("Just the table:\n - {}\n", fmt::join(table, "\n - "));
fmt::print("By id:\n - {}\n", fmt::join(left, "\n - "));
fmt::print("By name:\n - {}\n", fmt::join(right, "\n - "));
// find:
auto id2 = left.find(2);
auto nameTwo = right.find("two");
if (id2 != left.end())
fmt::print("id2: {}\n", *id2);
if (nameTwo != right.end())
fmt::print("nameTwo: {}\n", *nameTwo);
}
Printing the outbove shown above.
Advanced Topics/Usages
A few things to keep in mind:
if you really needed to share ownership of the data, you can, of course use shared_ptr<Data> instead
you can also construct multi-index containers over references/pointers, so I'd advice against shared_ptr unless you know it's actually required
more flexible key extraction mechanisms exist (e.g. you could have a non-unique ordered index on Record::data::ints::length())
you can have composite keys, which support partial querying on ordered indexes. This enable "database like" queries, see e.g. Boost multi-index container vs a multi-level mapping container based on std::unordered_map (map of maps) or Using boost multi index like relational DB
like with the standard containers, the key elements are const. In multi-index containers this implies that all accessors return const references. Refer to the document for modify, replace functions.
there's a project function to convert iterators between indexes, should you ever require this
Bonus: Less Verbose?
Or, with a clever macro to reduce repetition¹
#define Index(name) \
bmi::ordered_unique< \
bmi::tag<struct by_##name>, \
bmi::member<Record, decltype(Record::name), &Record::name>>
using Table = boost::multi_index_container<Record,
bmi::indexed_by<
Index(id),
Index(name)
> >;
¹ I was momentarily too lazy to make that template meta functions instead of the macro
This following code:
enum Type {Prince, Princess, King, Queen, NumTypes};
enum Country {England, Belgium, Netherlands, NumCountries};
class Factory {
static const std::array<std::array<int, NumTypes>, NumCountries> probabilities;
static std::array<std::array<int, NumTypes>, NumCountries> initializeProbabilities() {
std::array<std::array<int, NumTypes>, NumCountries> p;
p[England] = {29, 60, 80, 100};
p[Belgium] = {31, 66, 81, 100};
p[Netherlands] = {25, 45, 90, 100};
return p;
}
};
const std::array<std::array<int, NumTypes>, NumCountries> Factory::probabilities = initializeProbabilities();
is safe if I ever change the order of elements in enum Country, but it is not safe from any future reordering of enum Type elements. What is the best way to avoid that problem without initializing all 12 elements one by one?
In order to avoid dependency on the order, you should write something like:
p[England][Prince]=29;
p[England][Princess]=60;
p[England][King]=80;
p[England][Queen]=100;
p[Belgium][Prince]=31;
p[Belgium][Princess]=66;
p[Belgium][King]=81;
p[Belgium][Queen]=100;
This is the solution suggested by Brian (I think this is what he meant). Is this probably the best way to solve the issues described?
enum Type {Prince, Princess, King, Queen, NumTypes};
enum Country {England, Belgium, Netherlands, NumCountries};
class Factory {
static const std::array<std::map<Type, int>, NumCountries> probabilities;
static std::array<std::map<Type, int>, NumCountries> initializeProbabilities() {
std::array<std::map<Type, int>, NumCountries> p;
p[England] = { {Prince, 29}, {Princess, 60}, {King, 80}, {Queen, 100} };
p[Belgium] = { {Prince, 31}, {Princess, 66}, {King, 81}, {Queen, 100} };
p[Netherlands] = { {Prince, 25}, {Princess, 45}, {King, 90}, {Queen, 100} };
return p;
}
};
const std::array<std::map<Type, int>, NumCountries> Factory::probabilities = initializeProbabilities();
Or perhaps he meant map of a map.
I have a struct :
typedef struct
{
int nNum;
string str;
}KeyPair;
Then I initialize my struct into something like this:
KeyPair keys[] =
{
{0, "tester"},
{2, "yadah"},
{0, "tester"}
};
And yet, let's say a number of other initializations:
KeyPair keysA[] =
{
{0, "tester"},
{2, "yadah"},
{0, "tester"}
};
KeyPair keysB[] =
{
{0, "testeras"},
{2, "yadahsdf"},
{3, "testerasss"}
};
KeyPair OtherkeysA[] =
{
{1, "tester"},
{2, "yadah"},
{3, "tester"}
};
and like 20 more of 'em.
Now, how do I create another struct and initialize it such that it contains these initiazed KeyPairs?
The reason for this is because I will repetitively call a function whose parameters would come for these structs. And I DO NOT want to do it this way:
pressKeyPairs( keys, sizeof( keys) / sizeof( keys[0] ) );
pressKeyPairs( keysA, sizeof( keysA) / sizeof( keysA[0] ) );
pressKeyPairs( keysB, sizeof( keysB) / sizeof( keysB[0] ) );
pressKeyPairs( OtherkeysA, sizeof( OtherkeysA) / sizeof( OtherkeysA[0] ) );
and so on...
So I would like to just loop through a struct containing these inilialized instantiations of KeyPairs...
OR I would like to put these initialized instances of KeyPairs into a vector and just loop through the vector... How do I do that?
Assuming that you have a fixed number key pairs, you could use a structure member function:
typedef struct KeyPairs {
KeyPair keysA[3];
KeyPair keysB[3];
KeyPair otherKeysA[3];
void init() {
keysA[0].nNum = 0;
keysA[0].str = "tester";
keysA[1].nNum = 2;
keysA[1].str = "yadah";
keysA[2].nNum = 0;
keysA[2].str = "tester";
// and so on for other keys
}
} KeyPairs;
Then use it like so:
KeyPairs pairs;
pairs.init();
How about doing real C++ and using constructors ?
(note that typedefs are implicits for structs in C++)
struct KeyPair
{
int nNum;
string str;
public:
KeyPair() {}
KeyPair(int n, string s) : nNum(n), str(s) {}
};
And then use another struct :
struct TripleKeyPair
{
KeyPair keys[3];
TripleKeyPair()
{
// Your initialisation code goes here
}
};
And finally, I wouldn't advice using names such as :
KeysA, KeysB, KeysC ...
Arrays are exactly for this. Why note use std::vector ?
How about using "null" objects as delimiters in the array? You would have to use constructors though:
struct KeyPair
{
KeyPair() : fIsEmpty(true) {}
KeyPair(int nNum_, const char *szStr) : nNum(nNum_), str(szStr), fIsEmpty(false) {}
int nNum;
string str;
bool fIsEmpty;
};
Then you can initialize it like this:
KeyPair allKeys[] =
{
KeyPair(0, "testeras"),
KeyPair(2, "yadahsdf"),
KeyPair(3, "testerasss"),
KeyPair(),
KeyPair(0, "tester"),
KeyPair(2, "yadah"),
KeyPair(3, "tester"),
KeyPair(1, "moreyadah"),
KeyPair()
};
And the iteration is trivial if you implement a kind of strlen() analog for KeyPair object array.