2

There is an old legacy code which can not be modified. If simplified it looks like this:

enum class Type {A, B, C};
struct Object {Type type;};

Object* objs[N];
int count = 0;

#define addobj(ptr) objs[count++] = (Object*)ptr

struct A {
    Type type;
    int prop1;
    A(int v) : type(Type::A), prop1(v) {addobj(this);}
};
struct B {
    Type type;
    int prop1;
    B(int v) : type(Type::B), prop1(v) {addobj(this);}
};
struct C {
    Type type;
    int prop1;
    C(int v) : type(Type::C), prop1(v) {addobj(this);}
};

A* getA(int id) {return (A*)objs[id];}
B* getB(int id) {return (B*)objs[id];}
C* getC(int id) {return (C*)objs[id];}

In addition, there is "polymorphic" property access, which is allowed to change:

int& prop1ref(int id) {
    switch (objs[id]->type) {
    case Type::A: return getA(id)->prop1;
    case Type::B: return getB(id)->prop1;
    }
    return getC(id)->prop1;
}

void test() {
    A a(1); B b(2); C c(3);
    for (int id=0; id<count; id++)
        prop1ref(id);
}

Is there a way to replace only property access code in prop1ref with e.g. std::variant and std::visit?

Please note, prop1 name and type do match between classes, but locations (offsets) do not. Type field offset is guaranteed so cast can always be made. Also, new code should allow accessing double prop2, string prop3 etc in A, B, C classes without using macros.

kaylum
  • 13,833
  • 2
  • 22
  • 31
Rvd Boston
  • 21
  • 2

2 Answers2

1

Possibly something like this:

#include <variant>
#include <type_traits>
#include <iostream>

// ...

std::variant<A*, B*, C*> fetch_from_id(int const id) {
    switch (objs[id]->type) {
    case Type::A: return getA(id);
    case Type::B: return getB(id);
    default: return getC(id);
    }
}

void test() {
    A a(1); B b(2); C c(3);

    for (int id = 0; id < count; id++)
        std::visit([] (auto&& v) {
            using type = std::decay_t<decltype(v)>;
            if constexpr (std::is_same_v<type, A*>) {
                // For A
                std::cout << v->prop1 << std::endl;
            }
            if constexpr (std::is_same_v<type, B*>) {
                // For B
                std::cout << v->prop1 << std::endl;
            }
            if constexpr (std::is_same_v<type, C*>) {
                // For C
                std::cout << v->prop1 << std::endl;
            }
        }, fetch_from_id(id));
}

If you want to see for yourself if it works:

Demo

Ruks
  • 3,886
  • 1
  • 10
  • 22
  • Currently, as you use same code for both case, `[] (auto&& v) { std::cout << v->prop1 << std::endl; }` would be sufficient. – Jarod42 Sep 21 '21 at 08:34
  • Helper `overloaded` from [`std::visit`](https://en.cppreference.com/w/cpp/utility/variant/visit)'s example is a good alternative to `if constexpr`. – Jarod42 Sep 21 '21 at 08:35
  • Thanks! I guess there is no way to get rid of type switch cases in fetch_from_id code? – Rvd Boston Sep 21 '21 at 13:26
0

The below will allow you to extract other properties in a very easy way using lambdas:

#include <variant>
#include <string>
#include <iostream>


enum class Type {A, B, C};
struct Object {Type type;};

Object* objs[3];
int count = 0;

#define addobj(ptr) objs[count++] = (Object*)ptr

struct A {
    Type type;
    int prop1;
    A(int v) : type(Type::A), prop1(v) {addobj(this);}
};
struct B {
    Type type;
    int prop1;
    B(int v) : type(Type::B), prop1(v) {addobj(this);}
};
struct C {
    Type type;
    int prop1;
    C(int v) : type(Type::C), prop1(v) {addobj(this);}
};

A* getA(int id) {return (A*)objs[id];}
B* getB(int id) {return (B*)objs[id];}
C* getC(int id) {return (C*)objs[id];}

using var_t = std::variant<A*,B*,C*>;

var_t fetch_from_id(int const id) {
    switch (objs[id]->type) {
    case Type::A: return getA(id);
    case Type::B: return getB(id);
    default: return getC(id);
    }
}

// straightforward lambdas for extracting any property from your objects
auto get_prop1 = [](auto&& t){ return t->prop1;};
auto get_prop2 = [](auto&& t){ return t->prop2;};
auto get_prop3 = [](auto&& t){ return t->prop3;};


int main()
{
    A a(1); B b(2); C c(3);
    // print prop1
    for (int id=0; id<3; id++) std::cout << std::visit(get_prop1, fetch_from_id(id)) << " ";
    std::cout << std::endl;
    // print prop2
    // for (int id=0; id<3; id++) std::cout << std::visit(get_prop2, fetch_from_id(id)) << " ";
    // std::cout << std::endl;
    // print prop3
    // for (int id=0; id<3; id++) std::cout << std::visit(get_prop3, fetch_from_id(id)) << " ";
    // std::cout << std::endl;
}

Live code here

linuxfever
  • 3,763
  • 2
  • 19
  • 43