I have arrays of types int, bool and float:
std::array<int, 3>myInts = {15, 3, 6};
std::array<bool, 2>myBools = {true, false};
std::array<float,5>myFloats = {0.1, 15.2, 100.6, 10.44, 5.5};
I would like to generate a random integer(I know how to do that) from 0 to the total number of elements (3 2 5) so the generated random Integer represents one of the values. Next based on that integer I would like to retrieve my value and do further calculations with it. The problem I am facing is that I don't want to use if else
statements like these:
int randInt = RandIntGen(0, myInts.size() myBools.size() myFloats.size());//Generates a random Integer
if(randInt<myInts.size()){//if the random integer is less than the size of the integers array I can choose
// from the the integers array
int myValue = myInts[RandInt]
}
else if(randInt>=myInts.size() && randInt<myBools.size() myInts.size()){//if the random integer
//is between the size o the integer's array and the size of the bool's array the size of the integers array
//then I can choose from the bool's array
bool myValue = myBools(RandInt - myInts.size())
}
.
.
.
Then if for example randInt=2
then myValue=6
or if randInt=4
then myValue=false
However I would like that the selection algorithm was more straightforward something like:
int randInt = RandIntGen(0, myInts.size() myBools.size() myFloats.size());
allValues = {myInts, myBools, myFloats}
if(type_id(allValues[randInt]).name=="int")
int myValue = allValues[randInt] //(this value will be used for further calculations)
if(type_id(allValues[randInt]).name=="bool")
bool myValue = allValues[randInt] //(this value will be used for further calculations)
I've tried with a mix of templates, inheritance and linked lists however I cannot implement what I want. I think the solution should be really simple but at this time I cannot think of something else.
I am novice in C I've been learning already for 1 and half months, before I was doing stuff in python and everything was way easier but then I decided to try C . I am not a experienced programmer I know some basic things and I am trying to learn new things, thanks for the help.
CodePudding user response:
Most probably, you need to think how to satisfy your requirements in a simpler way, but it is possible to get literally what you want with C 17. If your compiler doesn't support C 17, you can use corresponding boost libraries. Here is the code:
#include <array>
#include <iostream>
#include <tuple>
#include <variant>
using Result = std::variant<int, bool, float>;
template<class T>
bool take_impl(int& i, const T& vec, Result& result)
{
if (i < static_cast<int>(std::size(vec)))
result = vec[i];
i -= std::size(vec);
return i < 0;
}
template<class T>
Result take(int i, const T& arrays)
{
if (i < 0)
throw std::runtime_error("i is too small");
Result res;
std::apply([&i, &res](const auto&... array) { return (take_impl(i, array, res) || ...); }, arrays);
if (i >= 0)
throw std::runtime_error("i is too large");
return res;
}
std::ostream& operator<<(std::ostream& s, const Result& v)
{
if (std::holds_alternative<int>(v))
std::cout << "int(" << std::get<int>(v);
else if (std::holds_alternative<bool>(v))
std::cout << "bool(" << std::get<bool>(v);
else
std::cout << "float(" << std::get<float>(v);
return std::cout << ')';
}
auto arrays = std::make_tuple(
std::array<int, 3>{15, 3, 6},
std::array<bool, 2>{true, false},
std::array<float,5>{0.1, 15.2, 100.6, 10.44, 5.5}
);
int main()
{
for (int i = 0; i < 10; i)
std::cout << take(i, arrays) << '\n';
}
If you are not required to keep separate arrays of different types, you can make one uniform array of std::variant<int, bool, float>
. This will be significantly more efficient than using std::shared_ptr
-s.
CodePudding user response:
You have to erase the type of your data so you can store them in one vector instead of three. For this to work you will have to encapsulate each type in a different class. You could also add the calculate method to these classes. In the end you will get something like this :
vector< data_ptr_t > v{ /*...*/ };
auto i = rand() % v.size();
v[i]->calculate();
You can use polymorphism to achieve type erasure. Here's a starting point:
#include <vector>
#include <memory>
using namespace std;
struct data_t
{
virtual void calculate() = 0; // this is an abstract method; override it in derived classes
};
struct int_t : public data_t
{
int v;
int_t(const int a) : v{ a } {}
void calculate() override
{
// calculations specific to int
}
};
struct bool_t : public data_t
{
bool v;
bool_t(const bool a) : v{ a } {}
void calculate() override
{
// calculations specific to bool
}
};
// etc
void calculate()
{
vector< shared_ptr<data_t> > v{ make_shared<int_t>(123), make_shared<bool_t>(true) };
auto i = rand() % v.size();
v[i]->calculate(); // this will call one of the above calculate methods, depending on which element of v was picked
}