0

I created a structure ready to receive iterators from diferent associative containers (set, map, multiset etc) and keep a pointer to this element.

set<ObjX> SetX {...}
set<ObjY> SetY {...}
map<ObjZ, Data> MapZ {...}

struct INPosition {
    INPosition(set<ObjX>::const_iterator pTMP)       : POINTER(&*pTMP) {}
    INPosition(set<ObjY>::const_iterator pTMP)       : POINTER(&*pTMP) {}
    INPosition(map<ObjZ, Data>::const_iterator pTMP) : POINTER(&*pTMP) {}

    void show() { cout << POINTER << '\t' << typeid(POINTER).name() << '\n'; }

    private:
       const void* POINTER;
};

INPosition TTT(SetY.find(ObjY(2)));

cout << "Addr SetY  Element 1: " << &*SetY.find(1) << '\t'<< typeid(&*SetY.find(1)).name() << '\n';
cout << "Addr SetY  Element 2: " << &*SetY.find(2) << '\t'<< typeid(&*SetY.find(2)).name() << '\n';
cout << "Addr POINTER (Elm 2): "; TTT.show();

The result are:

Addr SetY  Element 1: 0x5654ee2096d0      PKSt10ObjYI10MyClassSt14default_deleteIS0_EE
Addr SetY  Element 2: 0x5654ee209720      PKSt10ObjYI10MyClassSt14default_deleteIS0_EE
Addr POINTER (Elm 2): 0x5654ee209720      PKv                                          //The typeid.name supposes to be the same than Element 2 above???

POINTER keep the element address, but typeid doesn't recognize this element I supposed to (Same as element 2).

My question is how can create a function member that return the casted with the correct type? In other words, recover the iterator. Something like this:

auto Iterator() {
     return auto_cast(POINTER);
}
TheArchitect
  • 1,160
  • 4
  • 15
  • 26
  • 3
    Unrelated to your problem and question, but symbols starting with an underscore followed by an upper-case letter (like e.g. `_INPosition `) are reserved in all scopes for the compiler and standard library. Don't use them. For more information please see [What are the rules about using an underscore in a C++ identifier?](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier) – Some programmer dude Sep 05 '18 at 09:01
  • 2
    As for your problem, a `void*` doesn't have any kind of information about what it's pointing at. You, as the programmer, must know what it's pointing at or you just can't use the pointer for anything. And note that with `&*pTMP` you get a pointer to what the iterator is referencing, but not the iterator itself. The iterator is lost in your code (but depending on container it's not really useful to store iterators at all). – Some programmer dude Sep 05 '18 at 09:03
  • 4
    And this has the smell of [an XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). *Why* do you need this pointer? What is the *original* problem it's supposed to solve? – Some programmer dude Sep 05 '18 at 09:04
  • I need to create a vector of different elements from several sources (different containers) where I don't know the quantity of elements and their order. Keep the order of entrance is important. Because that I created the struct INPosition. Use a minimum of space is critical too, because that I bring the idea to use just a void* – TheArchitect Sep 05 '18 at 09:11
  • 1
    `auto_cast` reminds me of a[`DWIM-instruction`](http://www.catb.org/jargon/html/D/DWIM.html). – Deduplicator Sep 05 '18 at 09:13
  • @Tecoberg Given one element of this vector, how would you know what to do with it? It could be one of several completely unrelated types (a set element is fundamentally different from a map element)... – Max Langhof Sep 05 '18 at 09:14
  • The module where will receive the 'vector' will avaliate the elements (types) kept by INPosition + their order in the vector to take actions.... – TheArchitect Sep 05 '18 at 09:22
  • You are opening your self to a world of pain by using a `void` pointer. I strongly suggest you find a way to either template, use polymorphism, or store the pointer type. – Ælex Sep 05 '18 at 09:37

2 Answers2

3

You don't need a void*, you need a std::variant<ObjX*, ObjY*, ObjZ*>.

Your show() can use std::variant<...>::index() which will be 0, 1 or 2 for X,Y or Z.

MSalters
  • 173,980
  • 10
  • 155
  • 350
2

You cannot have such a thing as auto_cast. C++ does not store the type of objects converted to void* anywhere. You must know that type yourself to recover the object from the void*, as it's literally just a pointer to some memory location with no information what to find there.

You would have to store some information about the type next to the void*. For example, using an enum:

struct INPosition {
    enum class ObjType { SET_X, SET_Y, MAP_Z };

    INPosition(std::set<ObjX>::const_iterator pTMP)       : element(&*pTMP), objType(ObjType::SET_X) {}
    INPosition(std::set<ObjY>::const_iterator pTMP)       : element(&*pTMP), objType(ObjType::SET_Y) {}
    INPosition(std::map<ObjZ, Data>::const_iterator pTMP) : element(&*pTMP), objType(ObjType::MAP_Z) {}

private:
    const void* element;
    const ObjType objType;
};

That in itself is not enough to get you where you want though, because you cannot return different types from the same function. What return type would you expect auto Iterator() to have?

The lesson here is: By using void* you are explicitly defeating (or leaving) the C++ type system, but you can't write any useful C++ code outside of it. Getting back into the type system from void* takes some effort.

You could realistically make this work by using an (overloaded) functor argument to achieve something useful:

template<class OverloadedFunctor>
auto apply(OverloadedFunctor f)
{
    switch (objType)
    {
    case ObjType::SET_X:
        return f(*static_cast<const std::set<ObjX>::value_type*>(element));
    case ObjType::SET_Y:
        return f(*static_cast<const std::set<ObjY>::value_type*>(element));
    case ObjType::MAP_Z:
        return f(*static_cast<const std::map<ObjZ, Data>::value_type*>(element));
    }
}

Then if you had an overloaded functor that could do something useful with each of those types, you could apply it like this:

struct MyOverloadedFunctor
{
    void operator()(const ObjX& x) const;
    void operator()(const ObjY& y) const;
    void operator()(const std::pair<ObjZ, Data>& zd) const;
};

std::set<ObjY> setY;
setY.emplace();
INPosition position(setY.begin());
MyOverloadedFunctor myOverloadedFunctorInstance;
position.apply(myOverloadedFunctorInstance); // will call the second overload.

Demo

Max Langhof
  • 23,383
  • 5
  • 39
  • 72