Work in progress. The advantage this method has is that you don't have to cast anything when doing assignment, or any of the features listed below.
As of now it can:
- store non-container literal types (const char*, double, int, float, char, bool)
- output value for corresponding key with ostream operator
- reassign the value of an existing key
- add a new key:value pair using the append method only, key cannot be the same, or else you get an error message
- add literals of the same type with the + operator
In the code, I have demonstrated in the main function what it can currently do.
/*
This program demonstrates a map of arbitrary literal types implemented in C++17, using any.
*/
#include <vector>
#include <any>
#include <utility>
#include <iostream>
using namespace std;
class ArbMap
{
public:
ArbMap() : vec({}), None("None") {} //default constructor
ArbMap(const vector < pair<any,any> > &x) //parametrized constructor, takes in a vector of pairs
: vec(x), None("None") {}
//our conversion function, this time we pass in a reference
//to a string, which will get updated depending on which
//cast was successful. Trying to return values is ill-advised
//because this function is recursive, so passing a reference
//was the next logical solution
void elem(any &x, string &temp, int num=0 )
{
try
{
switch (num)
{
case 0:
any_cast<int>(x);
temp = "i";
break;
case 1:
any_cast<double>(x);
temp = "d";
break;
case 2:
any_cast<const char*>(x);
temp = "cc";
break;
case 3:
any_cast<char>(x);
temp = "c";
break;
case 4:
any_cast<bool>(x);
temp = "b";
break;
case 5:
any_cast<string>(x);
temp = "s";
break;
}
}
catch(const bad_cast& e)
{
elem(x,temp,++num);
}
}
//returns size of vector of pairs
size_t size()
{
return vec.size();
}
/* Uses linear search to find key, then tries to cast
all the elements into the appropriate type. */
any& operator[](any key)
{
ArbMap temp;
string stemp;
for (size_t i = 0; i<vec.size(); ++i)
{
temp.elem(vec[i].first,stemp);
if (stemp=="i")
{
try
{
any_cast<int>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<int>(key)==any_cast<int>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="d")
{
try
{
any_cast<double>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<double>(key)==any_cast<double>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="cc")
{
try
{
any_cast<const char*>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<const char*>(key)==any_cast<const char*>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="c")
{
try
{
any_cast<char>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<char>(key)==any_cast<char>(vec[i].first))
{
return vec[i].second;
}
}
else if (stemp=="b")
{
try
{
any_cast<bool>(key);
}
catch(const bad_cast& e)
{
continue;
}
if (any_cast<bool>(key)==any_cast<bool>(vec[i].first))
{
return vec[i].second;
}
}
}
//vec.push_back({key,None});
throw -1;
//return None;
}
void print();
void append(any key, any value);
private:
vector < pair<any,any> > vec;
any None;
};
ostream& operator<<(ostream& out, any a)
{
ArbMap temp; //should be updated to be a smart pointer?
string stemp;
temp.elem(a,stemp); //stemp will get updated in the elem function
//The "if else-if ladder" for casting types
if (stemp=="i") out << any_cast<int>(a);
else if (stemp=="d") out << any_cast<double>(a);
else if (stemp=="cc") out << any_cast<const char*>(a);
else if (stemp=="c") out << any_cast<char>(a);
else if (stemp=="b")
{
if (any_cast<bool>(a)==1)
out << "true";
else
out << "false";
}
else if (stemp=="s") out << any_cast<string>(a);
return out;
}
any operator+(any val1, any val2)
{
ArbMap temp;
string stemp1, stemp2;
temp.elem(val1,stemp1);
temp.elem(val2,stemp2);
try
{
if (stemp1 != stemp2)
throw -1;
if (stemp1 == "i")
{
return any_cast<int>(val1)+any_cast<int>(val2);
}
else if (stemp1 == "d")
{
return any_cast<double>(val1)+any_cast<double>(val2);
}
else if (stemp1 == "cc")
{
return string(any_cast<const char*>(val1))+string(any_cast<const char*>(val2));
}
else if (stemp1 == "c")
{
return string{any_cast<char>(val1)}+string{any_cast<char>(val2)};
}
else if (stemp1=="b")
{
return static_cast<bool>(any_cast<bool>(val1)+any_cast<bool>(val2));
}
}
catch (int err)
{
cout << "Bad cast! Operands must be of the same 'type'.\n";
}
return val1;
}
void ArbMap::print()
{
cout << '\n';
for (size_t i = 0; i<vec.size(); ++i)
{
cout << vec[i].first << ": " << vec[i].second << '\n';
}
cout << '\n';
}
void ArbMap::append(any key, any value)
{
try
{
(*this)[key];
throw "Already exists!";
}
catch(int error)
{
vec.push_back({key,value});
}
catch(const char* error)
{
cout << "ArbMap::append failed, key already exists!\n";
}
}
int main() {
ArbMap s({{1,2},{"aaa",1.2},{'c',33.3},{"what","this is awesome"}, {true, false}});
cout << s[1] << '\n' << s["aaa"] << '\n' << s['c']
<< '\n' << s["what"] << '\n'
//Uncomment the line below and runtime error will occur, as
//entry is not in the dictionary
// << s["not in the dictionary bro"] << '\n'
<< s[true] << '\n';
s.print();
s[1] = "hello";
s.print();
s.append(2.3,"what");
s.print();
s[2.3] = "hello";
s.print();
s.append(2.3,"what");
s.print();
s[1] = 1.2;
s.print();
s.append(2.4,1.2);
//Operator +
cout << s[1]+s[2.4] << '\n';
cout << s["what"] + s[2.3] << '\n';
s.append('d','a');
cout << s['c'] << '\n';
cout << s[2.4]+ s["aaa"]+ s['c'] + s['c'] + s['c'] << '\n';
cout << s[true]+s[true] << '\n';
return 0;
}
boost::any
. – Brilliantinestd::map<std::string, std::shared_ptr<void>>
. – Impermissiblevoid *
(shared_ptr<void>
is the same). You'd need at least an extra value to figure out what that thing is. I'd either use a pointer to a base class orboost::variant<>
if that's unavailable, unless you're writing really low level code. – GriffeyMyField<T>
, I guess another option is a base class that eachMyField<T>
inherits from. – Brilliantine