Last active
August 1, 2024 08:18
-
-
Save Mr-Andersen/cd5f9d8740e03b3a779bd49da281652d to your computer and use it in GitHub Desktop.
Implementation of tagged union without typeinfo library. Beware: this code contains a dozen undefined behaviours and a dozen more. Use only as inspiration.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
// get_type<i, Ts...>::type -> Ts[i] (Compile error of i >= length(Ts)) | |
template<size_t i, typename T, typename... Ts> | |
struct get_type { | |
typedef typename get_type<i - 1, Ts...>::type type; | |
}; | |
template<typename T, typename... Ts> | |
struct get_type<0, T, Ts...> { | |
typedef T type; | |
}; | |
// Ts[index_type<A, Ts...>::value] == A (Compilation error if A not in Ts) | |
template<typename A, typename T, typename... Ts> | |
struct index_type { | |
static const size_t value = index_type<A, Ts...>::value + 1; | |
}; | |
template<typename A, typename... Ts> | |
struct index_type<A, A, Ts...> { | |
static const size_t value = 0; | |
}; | |
template<typename T> | |
constexpr T max(const T& a, const T& b) { | |
return a > b ? a : b; | |
} | |
// max_size<Ts...>::value | |
template<typename T, typename... Ts> | |
struct max_size { | |
static const size_t value = max(sizeof(T), max_size<Ts...>::value); | |
}; | |
template<typename T> | |
struct max_size<T> { | |
static const size_t value = sizeof(T); | |
}; | |
template<typename... Ts> | |
class Variant { | |
size_t curr_type; | |
char data[max_size<Ts...>::value]; | |
public: | |
Variant(): | |
curr_type(), | |
data() {} | |
template<typename T> | |
Variant(const T& x): | |
curr_type(index_type<T, Ts...>::value) | |
{ | |
auto ptr = reinterpret_cast<const char*>(&x); | |
std::copy(ptr, ptr + sizeof(T), this->data); | |
} | |
template<typename T> | |
const T& operator=(const T& x) { | |
this->curr_type = index_type<T, Ts...>::value; | |
auto ptr = reinterpret_cast<const char*>(&x); | |
std::copy(ptr, ptr + sizeof(T), this->data); | |
return x; | |
} | |
template<typename T> | |
bool is() const { | |
return this->curr_type == index_type<T, Ts...>::value; | |
} | |
template<typename T> | |
T& as() { | |
if (!this->is<T>()) | |
throw std::runtime_error("Requested type is not contained"); | |
return *reinterpret_cast<T*>(this->data); | |
} | |
template<typename T> | |
bool into(T& x) { | |
if (!this->is<T>()) | |
return false; | |
x = *reinterpret_cast<T*>(this->data); | |
return true; | |
} | |
}; | |
int main() { | |
Variant<short unsigned int, long int [3]> v; | |
v = (long int [3]) {10, -123, 345}; | |
if (v.is<long int [3]>()) { | |
std::cout << v.as<long int [3]>()[0] << std::endl; | |
} | |
} |
i don't think, that you should delete the gist.
It was helpful to me, because it is a simple (=short) and therefore understandable implementation of a variant type.
I think these comments (or a comment in the original source) are enough to document the possible problems with this code and the things a user of this might want to think about
I agree, this Gist was very helpful in me solving an identical problem I had in passing sensor data around on an RP2040/Arduino platform.
I never full understood the templating language you used, but it inspired me to work on a slightly different templated approach for a variant KeyValue pair propery set:
PropertySet props = PropertySet();
// Set the property key/value pair
props.setProperty("IntProp", 12345);
props.setProperty("StrProp", std::string("A string property"));
props.setProperty("ArrProp", std::array<int, 5>({1,2,3,4,5}));
// Get the property values based on the key
auto intProp = props.getProperty<int>("IntProp");
auto strProp = props.getProperty<std::string>("StrProp");
auto arrProp = props.getProperty<std::array<int, 5>>("ArrProp");
// Get the underlying Property descriptor
PropertyType::shared_ptr propType = props.getPropertyType("IntProp");
if(propType->typeOf<int>()){
std::shared_ptr<PropertyDescriptor<int>> propertyDesc;
propertyDesc = propType->getDescriptor<int>();
Serial.print("\nIntProp Descriptor Value: ");
Serial.print(propertyDesc->value());
}
Full Gist here: https://gist.github.com/nstansbury/6d627a447ca3b4ebbcf2c49ae37922c5
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Oh yes, it definitely has problems. You are probably right about alignment, also this thing does not call destructor on inner object (that's easy to fix though). You are right about
const
stuff. This could also use a move-constructor.I suspect there are many more problems with this code, so I'd rather erase this gist at all, actually :) I've written it when I knew C++ less, and the more you know it, more insecure you get about your C++ code. I don't want novices get confused by it.
Thank you for writing back and I am happy it helped you!