Initializing static 2D array - c++

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.

Related

Understand std::map::insert & emplace with hint

map insert comparison
Question> I try to understand the usage of insert & emplace with hint introduced to std::map. During the following test, it seems to me that the old fashion insert is fastest.
Did I do something wrong here?
Thank you
static void MapEmplaceWithHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
auto where(std::end(mapInt));
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
where = mapInt.emplace_hint(where, n, n+1);
}
}
}
BENCHMARK(MapEmplaceWithHint);
static void MapInsertWithHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
auto where(std::end(mapInt));
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
where = mapInt.insert(where, {n, n+1});
}
}
}
BENCHMARK(MapInsertWithHint);
static void MapInsertNoHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
mapInt.insert({n, n+1});
}
}
}
BENCHMARK(MapInsertNoHint);
static void MapReverseInsertNoHint(benchmark::State& state) {
std::vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12};
std::map<int, int> mapInt;
for (auto _ : state) {
for (const auto &n : v) { // Items in incremental order
mapInt.insert({n, n+1});
}
}
}
BENCHMARK(MapReverseInsertNoHint);
static void MapEmplaceNoHint(benchmark::State& state) {
std::vector<int> v{12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
std::map<int, int> mapInt;
for (auto _ : state) {
for (const auto &n : v) { // Items in non-incremental order
mapInt.emplace(n, n+1);
}
}
}
BENCHMARK(MapEmplaceNoHint);
First, let's create a dataset more meaningful than 12 integers:
std::vector<int> v(10000);
std::iota(v.rbegin(), v.rend(), 0);
Results from all functions are now more comparable: https://quick-bench.com/q/HW3eYL1RaFMCJvDdGLBJwEbDLdg
However, there's a worse thing. Notice that looping over state makes it perform the same operations several times to measure the average time. But, since you are reusing the same map, each insert or emplace after the first loop iteration is failing, so you mostly measure time of failed inserts, where hint doesn't help.
Test cases should look more like this:
std::vector<int> v(1000);
std::iota(v.rbegin(), v.rend(), 0);
for (auto _ : state) {
std::map<int, int> mapInt;
auto where(std::end(mapInt));
for (const auto &n : v) { // Items in non-incremental order
where = mapInt.emplace_hint(where, n, n+1);
}
}
And with this, hints start to shine (had to limit data to 1000, otherwise I'd get timeouts): https://quick-bench.com/q/2cR4zU_FZ5HQ6owPj9Ka_y9FtZE
I'm not sure if the benchmarks are correct, but quick glance in the assembly suggests that inserts were not optimized altogether, so there's a chance it's good enough.
As noticed by Ted Lyngmo, try_emplace() with hint tends to perform (slightly) better:
https://quick-bench.com/q/evwcw4ovP20qJzfsyl6M-_37HzI

From template parameter to constructor argument

Does C++17 (or earlier but not c++20) allow this?
I need a type_traited conditional class, like a bitset with an internal 32 or 64 unsigned integer storage, depending if the template argument N is lesser then 32 or greater (please forget about more than 64 bits).
But the constraint is to finally implement two and only two classes for all possible cases. Next source code defines the problem using static and running time asserts:
Coliru link: http://coliru.stacked-crooked.com/a/d53a5b00bd828fb5
#include <cassert>
#include <iostream>
#include <type_traits>
struct bitset32
{
bitset32() : bits(0) { }
bitset32(int _bits) : bits(_bits) { }
const int bits;
uint32_t value;
};
struct bitset64
{
bitset64() : bits(0) { }
bitset64(int _bits) : bits(_bits) { }
const int bits;
uint64_t value;
};
template <int N>
using bitset = std::conditional_t<(N<=32), bitset32, bitset64>;
int main ()
{
static_assert(std::is_same<bitset<1>, bitset<2>>::value);
static_assert(std::is_same<bitset<33>, bitset<34>>::value);
static_assert(!std::is_same<bitset<1>, bitset<33>>::value);
bitset<1> var1;
bitset<15> var2;
bitset<32> var3;
bitset<64> var4;
assert(var1.bits == 1);
assert(var2.bits == 15);
assert(var3.bits == 32);
assert(var4.bits == 64);
}
Any solution is welcomend even if it changes the basic types and uses inheritance or whatever other mechanism necessary, but please, do not offer using a template function returning an object in the style of template<int N> make_bitset { return Bitset<N>(N); } because it is needed to implement variables using this constructo Bitset<N> variable_name.
A new hypothesis, CTAD based
#include <cstdint>
#include <iostream>
#include <type_traits>
template <std::size_t>
struct BitSet;
template <>
struct BitSet<32u>
{
template <typename ... Ts>
BitSet (Ts...) {}
std::uint32_t value;
};
template <>
struct BitSet<64u>
{
template <typename ... Ts>
BitSet (Ts...) {}
std::uint64_t value;
};
template <typename ... Ts>
BitSet (Ts...) -> BitSet<(32 < sizeof...(Ts) ? 64u : 32u)>;
int main()
{
auto b1 = BitSet{1};
auto b2 = BitSet{2};
auto b3 = BitSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39};
auto b4 = BitSet{0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47, 48, 49};
static_assert( std::is_same_v<decltype(b1), decltype(b2)> );
static_assert( std::is_same_v<decltype(b1), BitSet<32u>> );
static_assert( std::is_same_v<decltype(b3), decltype(b4)> );
static_assert( std::is_same_v<decltype(b3), BitSet<64u>> );
}
You can just make bitset32 and bitset64 templates like
template <std::size_t BitsUsed>
struct bitset32
{
bitset32() : bits(BitsUsed) { }
bitset32(int _bits) : bits(_bits) { }
const int bits;
uint32_t value;
};
template <std::size_t BitsUsed>
struct bitset64
{
bitset64() : bits(BitsUsed) { }
bitset64(int _bits) : bits(_bits) { }
const int bits;
uint64_t value;
};
and now the default constructor will pull in the correct number of bits. Then you just need to modify your alias like
template <int N>
using bitset = std::conditional_t<(N<=32), bitset32<N>, bitset64<N>>;
and you get the behavior you are asking for.
This will not work if you want to store a bitset<5> in the same container as a bitset<32> though since different template instantiations create distinct type.

Pass array of structs by reference

I would like to pass an array of structs to a function.
struct Month {
std::string name;
int days;
};
Month months[12]{{"January", 31}, {"February", 28}, {"March", 31}, {"April", 30}, {"May", 31}, {"June", 30},
{"July", 31}, {"August", 31}, {"September", 30}, {"October", 31}, {"November", 30}, {"December", 31}};
I have tried to pass the months array to functions with the parameter: Month (&months)[]
The compiler noted: "no known conversion for argument 1 from ‘main()::Month [12]’ to ‘Month (&)[12]’".
I then attempted to pass the array by a pointer and to do this I first allocated the elements of the months array on the free-store as shown below.
Month* months[12]{new Month{"January", 31}, new Month{"February", 28}, new Month{"March", 31}, new Month{"April", 30}, new Month{"May", 31}, new Month{"June", 30},
new Month{"July", 31}, new Month{"August", 31}, new Month{"September", 30}, new Month{"October", 31}, new Month{"November", 30}, new Month{"December", 31}};
I tried to pass this array to a function with the parameter: Month**
The compiler stated: "no known conversion for argument 1 from ‘main()::Month* [12]’ to ‘Month**’"
I would like to know how I could pass my months array by pointer and if it is possible to pass it by reference as well.
And yes, I do know that using a vector would be a lot easier but I just started learning C++ a few days ago and I would like to become familiarized with the arrays.
The function itself is empty, I cannot write the function without knowing what my parameter will be.
void print_table(Month** months) {
}
And the function call is:
print_table(months)
After removing all extraneous code I was left with this:
struct Month;
void print_table(Month** months);
int main() {
struct Month {
std::string name;
int days;
}
Month* months[12]{new Month{"January", 31}, new Month{"February", 28}, new Month{"March", 31}, new Month{"April", 30}, new Month{"May", 31}, new Month{"June", 30},
new Month{"July", 31}, new Month{"August", 31}, new Month{"September", 30}, new Month{"October", 31}, new Month{"November", 30}, new Month{"December", 31}};
print_table(months);
}
The issue was explained in the comments by john, I have also posted the corrected code.
Since you will treat the content of Month as constant, simply pass a rvalue reference, e.g. Month*&& m. Example:
void showmonths (Month*&& m, size_t nmonths)
{
for (size_t i = 0; i < nmonths; i++)
std::cout << std::left << std::setw(12) << m[i].name << m[i].days << '\n';
}
For further details on the declaration and use of references, see: Reference declaration
After reading john's comment I realized that the issue was that my struct was declared inside of the main function as shown here:
struct Month;
void print_table(Month** months);
int main() {
struct Month {
std::string name;
int days;
}
Month* months[12]{new Month{"January", 31}, new Month{"February", 28}, new Month{"March", 31}, new Month{"April", 30}, new Month{"May", 31}, new Month{"June", 30},
new Month{"July", 31}, new Month{"August", 31}, new Month{"September", 30}, new Month{"October", 31}, new Month{"November", 30}, new Month{"December", 31}};
print_table(months);
}
I edited to declare the struct outside of the main function and it worked. Thanks!
struct Month {
std::string name;
int days;
}
void print_table(Month** months);
int main() {
Month* months[12]{new Month{"January", 31}, new Month{"February", 28}, new Month{"March", 31}, new Month{"April", 30}, new Month{"May", 31}, new Month{"June", 30},
new Month{"July", 31}, new Month{"August", 31}, new Month{"September", 30}, new Month{"October", 31}, new Month{"November", 30}, new Month{"December", 31}};
print_table(months);
}
Try this
Month months[10];
//like this you can pass
print_table(months);
Pass to this function
void print_table(struct Month * months)
{
//code here
}

How do I access list of structs with an integer argument (c++)?

I have a series of structs:
const struct weapon Dagger = { 1, 40, 5, 20, 30, "Dagger" };
const struct weapon Sword = { 2, 35, 25, 40, 60, "Sword" };
const struct weapon Axe = { 4, 50, 10, 70, 80, "Axe" };
const struct ...
I want to arrange them so I can access each one by integer. I am trying to build a function that takes int x and int y as arguments, and returns the indexed struct's data. For example, if the function takes 2 and 3, respectively, the value 35 from the weapon struct will be returned. Intuitively, I imagined the function body looking something like return weapon[x].y, although this does not work.
I do not know how to do this. The best alternative I can see is to use arrays instead of structs. Should I do this instead?
If you are only after the numeric data, then you could use two arrays: one for the data itself, and one for accessor function pointers:
struct weapon
{
int val;
int hp;
int foo;
// ...
std::string name;
};
const weapon w[] = {
{ 1, 40, 5, 20, 30, "Dagger" },
{ 2, 35, 25, 40, 60, "Sword" },
{ 4, 50, 10, 70, 80, "Axe" },
};
using accessor = int weapon::*;
const accessor acc[] = {
&weapon::val,
&weapon::hp,
&weapon::foo,
// ...
};
Now to look up property j in weapon i (both zero-indexed), you could say:
w[i].*acc[j]
Alternatively, you could perhaps represent your data as an array of pairs:
std::pair<const char*, std::array<int, 5>> w_alt[] = {
{ "Dagger", { 1, 40, 5, 20, 30 } },
{ "Sword", { 2, 35, 25, 40, 60 } },
{ "Axe", { 4, 50, 10, 70, 80 } },
};
Now the ith weapon's anme is w_alt[i].first, and its jth property is w_alt[i].second[j].
If you want a container that has indexed access, you probably want std::vector. Something like:
std::vector<weapon> WeaponVector;
For the first part of your question (selecting weapon based on index), using an array is a simple option, e.g.:
std::array<weapon, 3> const weapons =
{ { 1, 40, 5, 20, 30, "Dagger" }
, { 2, 35, 25, 40, 60, "Sword" }
, { 4, 50, 10, 70, 80, "Axe" }
};
Then, if you want, you can make specific references:
weapon const &Sword = weapons[1];
or make indices which you'll use to access the array:
enum weapon_id { Dagger, Sword, Axe };
// ...
do_something( weapons[Sword] );
Using std::array instead of a C-style array weapon const weapons[] =
has a drawback that you can't deduce the dimension, but it has other benefits that make up for this.
For the second part, you may add a function that looks up the weapon properties by index. For example:
int weapon_lookup_number(weapon const &w, int index)
{
switch(index)
{
case 0: return w.id;
case 1: return w.power;
// etc.
}
}
with another function for looking up the string.
Using template specialization it'd be possible to have a function that looks up the member by index and evaluates to the correct type, however that would only work in the case of the index being known at compile-time.
If your struct becomes:
struct weapon {
int val;
int hp;
int foo;
// ...
std::string name;
};
You could use a std::vector<weapon> Weapons to store the structs, sort it using:
std::sort(Weapons.begin(), Weapons.end(),
[](const Weapons& w1,const Weapons& w2) -> bool { return w1.val > w2.val; }))
and access them using the vector indexes like: weapons[some_num].hp;, where some_num will correspond to the wanted val.

Looking for an easy way to reinitialize a struct

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")));