I have a complicated structs that evolved from version to version, adding/removing fields. For backward compatibility, all the versions are kept in the code, leading to something like this :
struct MyDataV0{
int a;
.....
};
struct MyDataV1{
//int a disappeared for this version
double d; //this fields was added to this version
.....
};
I want to provide a unified view of this struct that is version agnostic. Is there a pattern or best practices to do this?
You can also take a closer look at creating a flexible container, for example, in C++ 17 you can use std::map with std::variant values.
Its definition is unified and version-agnostic.
#include <string>
#include <map>
#include <iostream>
#include <variant>
int main()
{
std::map<std::string, std::variant<int, double, std::string>> st;
st["version"] = 2;
st["field_int_v1"] = 1;
st["field_double_v2"] = 1.1; // added in version 2
st["field_string_v2"] = "1.1 in text";
//..
// process somewhere
std::cout << std::get<int>(st["field_int_v1"] ) << std::endl;
if (std::get<int>(st["version"]) == 2){
std::cout << std::get<double>(st["field_double_v2"] ) << std::endl;
std::cout << std::get<std::string>(st["field_string_v2"]) << std::endl;
}
return 0;
}
This may not provide a 'unified view', the typical approach to handling multiple versions is with namespacing:
namespace my_data
{
namespace v1
{
// v1
struct MyData { … };
}
inline namespace v2
{
// v2
struct MyData { … };
}
}
// To use v2
my_data::MyData data = {};
// or
my_data::v2::MyData data1 = {};
// To use v1
my_data::v1::MyData data2 = {};
By default, you will use the version of the struct which is in the inline namespace, if you want to reference an older (possibly deprecated) version, you will have to be explicit. You can then overload any function which consumes MyData, in the same namespace as the struct and rely on ADL to find the correct overload.
Related
I started learning C++ and I made a simple thing like printing variables etc, but I wanted to make a new value on a variable like in Python:
test = "hello world"
print(test)
test = 5
print(test + 6)
So I had this:
string test = "hello world";
cout << test << "\n";
And now I wanted to assign a number to test, so I used int test = 5;, but I got an error:
redefinition of 'test' with a different type
Is it possible to assign a new type to a variable somehow?
is it possible to assign a new type to a variable somehow?
A new type, no. C++ is a statically typed language. Variable types are specified at compile-time and cannot change at runtime.
For what you are asking, the closest thing available is std::variant in C++17 and later, which is a fixed class type that can hold different kinds of values at runtime, eg:
#include <iostream>
#include <string>
#include <variant>
using myVariant = std::variant<int, std::string>;
void print(const myVariant &v)
{
std::visit([](const auto &x) { std::cout << x; }, v);
std::cout << "\n";
}
int main()
{
myVariant test;
test = "hello world";
print(test);
test = 5;
print(test);
return 0;
}
Online Demo
In C++17 and newer, std::variant is a type that can be used to store multiple different types of data. Though you'll have to do a bit of finagling with visitors to get the polymorphic print you know from Python.
#include <variant>
#include <string>
#include <iostream>
struct PrintVisitor {
template <typename T>
void operator()(const T& arg) {
std::cout << arg << std::endl;
}
};
int main() {
std::variant<std::string, int> test = "hello world";
std::visit(PrintVisitor(), test);
test = 5;
std::visit(PrintVisitor(), test);
}
Suppose I have next code, that is used for simply storing reference/pointer to objects of types A, B or C. I actually don't need complete types.
Now i have the following solution, where I need a lot of #include bloat.
Header:
using MyVariant = std::variant<class A, class B, class C, ...>;
class Holder {
public:
Holder(MyVariant &&TheValue);
const MyVariant &GetValue();
private:
std::unique_ptr<MyVariant> Value;
};
Source file:
#include "A.hpp"
#include "B.hpp"
#include "C.hpp"
Holder::Holder(MyVariant &&TheValue)
: Value(std::make_unique<MyVariant>(std::move(TheValue)) {}
const MyVariant &Holder::GetValue { return Value; }
How I can implement the same semantics without all instantiated types as std::variant template parameters / dynamic memory allocation / dynamic polymorphism?
As others have pointed out the specifications seem a little contradicting.
I can only think of variable template arguments, to achieve not having to specify all possible classes for the Holder class.
Example
#include <iostream>
#include <memory>
#include <variant>
// h
template<typename ...T>
class Holder {
using variant = std::variant<T...>;
public:
Holder(variant &&TheValue)
:Value(std::make_unique<variant>(std::move(TheValue)))
{
};
template<typename TARGET>
const TARGET &GetValue() const {
return std::get<TARGET>(*Value);
};
private:
std::unique_ptr<variant> Value;
};
// main.cpp testing
int main() {
auto a = Holder<int, float>(5);
const auto v = a.GetValue<int>();
std::cout << "holding int: " << v << std::endl;
a = Holder<int, float>(5.5f);
const auto v2 = a.GetValue<float>();
std::cout << "holding float: " << v2 << std::endl;
}
I think at that point it looks like a rather redundant class though.
I have a class that has a constructor. I now need to make a map with it as a value how do I do this? Right now without a constructor I do.
#include <iostream>
#include <map>
using namespace std;
class testclass {
public:
int x = 1;
};
int main()
{
map<int,testclass> thismap;
testclass &x = thismap[2];
}
If I added a constructor with arguments how would I add them to the map? I basically need to do
#include <iostream>
#include <map>
using namespace std;
class testclass {
public:
int x = 1;
testclass(int arg) {
x = arg;
}
};
int main()
{
map<int,testclass> thismap;
testclass &x = thismap[2];
}
This obviously wouldn't work since it requires an argument but I can't figure a way of doing this.
This is how you can add items of your own class to your map.
Note : I used a string in testclass to better show difference
between key and value/class.
#include <iostream>
#include <string>
#include <map>
class testclass
{
public:
explicit testclass(const std::string& name) :
m_name{ name }
{
};
const std::string& name() const
{
return m_name;
}
private:
std::string m_name;
};
int main()
{
std::map<int, testclass> mymap;
// emplace will call constructor of testclass with "one", and "two"
// and efficiently place the newly constructed object in the map
mymap.emplace(1, "one");
mymap.emplace(2, "two");
std::cout << mymap.at(1).name() << std::endl;
std::cout << mymap.at(2).name() << std::endl;
}
Using std::map::operator[] requires that the mapped type is default-constructible, since it must be able to construct an element if one doesn't already exist.
If your mapped type is not default-constructible, you can add elements with std::map::emplace, but you still can't use std::map::operator[] to search, you will need to use std::map::find() or so.
That's a rather obvious feature of std::map (and very similar other std containers). Some of their operations require specific type requirements for good reasons.
There is no problem to create such a map as you suggest in the first place, however, you are restricted to method calls that do not require potential default construction. The operator[] is such a method, since in the case the element is not found, it is created. That is what does not work in your example. Just use other methods with little impact on the map usage and you can still succeed:
#include <iostream>
#include <map>
using namespace std;
class testclass {
public:
int x = 1;
testclass(int arg) {
x = arg;
}
};
int main()
{
map<int,testclass> thismap;
thismap.insert( {2, testclass(5)} );
auto element2 = thismap.find(2);
if (element2 != thismap.end()) {
testclass& thiselement = element2->second;
cout << "element 2 found in map, value=" << thiselement.x << endl;
}
auto element5 = thismap.find(5);
if (element5 == thismap.end()) {
cout << "no element with key 5 in thismap. Error handling." << endl;
}
}
Main issue: avoid operator[].
Note:
Looking at the other very good answers, there are a lot of methods that can be used without default construction. There is not "right" or "wrong" since this simply depends on your application. at and emplace are prime examples that are highly advisable.
I'm new to C++ and very confused on how to approach this. In Javascript, I can do something like this to access an object dynamically very easily:
function someItem(prop) {
const item = {
prop1: 'hey',
prop2: 'hello'
};
return item[prop];
}
In C++, I'm assuming I have to use a Struct, but after that I'm stuck on how to access the struct member variables dynamically.
void SomeItem(Property Prop)
{
struct Item
{
Proper Prop1;
Proper Prop2;
};
// Item[Prop] ??
}
This could be terrible code but I'm very confused on how to approach this.
This is a simple example of how to create an instance of a struct and then access its members:
#include <iostream>
#include <string>
struct Item {
std::string prop1 = "hey";
std::string prop2 = "hello";
};
int main() {
Item myItem;
std::cout << myItem.prop1 << std::endl; // This prints "hey"
std::cout << myItem.prop2 << std::endl; // This prints "hello"
return 0;
}
As mentioned in the comments, it looks like you might want a map. A map has keys and values associated with them, as an example you could have a key "prop1" be associated with a value "hey":
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, std::string> myMap;
myMap["prop1"] = "hey";
myMap["prop2"] = "hello";
std::cout << myMap["prop1"] << std::endl; // This print "hey"
std::cout << myMap["prop2"] << std::endl; // This print "hello"
return 0;
}
The first would be considered "normal" struct usage in C++ and the other is more applicable to cases where you have to look things up by keys
As mentioned in a comment, in C++ you would not define a custom structure for this, but rather use a std::unordered_map. I don't know Javascript, though if Property is an enum (it could be something else with small modifications) and return item[prop]; is supposed to return a string, then this might be close:
#include <string>
#include <unordered_map>
#include <iostream>
enum class Property { prop1,prop2};
std::string someItem(Property p){
const std::unordered_map<Property,std::string> item{
{Property::prop1,"hey"},
{Property::prop2,"hello"}
};
auto it = item.find(p);
if (it == item.end()) throw "unknown prop";
return it->second;
}
int main(){
std::cout << someItem(Property::prop1);
}
std::unordered_map does have a operator[] that you could use like so return item[p];, but it inserts an element into the map when none is found for the given key. This is not always desirable, and not possible when the map is const.
Does there exist a Boost Hana method for compile-time converting the types of members of a Struct concept to a STL container of std::string's of the typenames?
For example,
MyType t();
std::array<std::string, 3> ls = boost::hana::typesToString(t);
for(std::string x : ls){
std::cout << x << std::endl;
}
Yields "int string bool" to STDOUT,
With
class MyType{
int x;
std::string y;
bool z;
}
The documentation clearly provides methods for getting the members and their values of an instance of a Struct concept, but I haven't found anything there that does this for the types of the members. A simpler task would be to do:
int x;
std::string tName = boost::hana::typeId(x); //tName has value "int"
I've read this post but I'd like to know if there's a clean way out-of-the-box in Hana. Even better would be a way to iterate through the members of the Struct without having to know them by name.
If you are using Clang, Hana has an experimental feature hana::experimental::type_name. This can be used to get the type-names of the members of the struct:
#include <boost/hana.hpp>
#include <boost/hana/experimental/type_name.hpp>
namespace hana = boost::hana;
template <typename Struct>
auto member_type_names() {
constexpr auto accessors = hana::accessors<Struct>();
return hana::transform(
accessors,
hana::compose(
[](auto get) {
using member_type
= std::decay_t<decltype(get(std::declval<Struct>()))>;
return hana::experimental::type_name<member_type>();
},
hana::second
)
);
}
Demo (live on Wandbox):
#include <iostream>
#include <string>
struct MyType {
int a;
std::string b;
float c;
};
BOOST_HANA_ADAPT_STRUCT(MyType, a, b, c);
int main() {
hana::for_each(member_type_names<MyType>(), [](auto name) {
// Note that the type of `name` is a hana::string, not a std::string
std::cout << name.c_str() << '\n';
});
}
Outputs:
int
std::__1::basic_string<char>
float