Skip to content

Instantly share code, notes, and snippets.

@Mr-Andersen
Last active August 1, 2024 08:18
Show Gist options
  • Save Mr-Andersen/cd5f9d8740e03b3a779bd49da281652d to your computer and use it in GitHub Desktop.
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.
#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;
}
}
@nstansbury
Copy link

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