How to allow multiple struct definitions in single argument ?

closed account (GbRGwA7f)
I want my void to allow multiple struct definitions from a single argument lets say thats "myStructAny" but I can put there myStructA and myStructB. Is that possible ?

EXAMLE:
-------

STRUCTS:
1
2
3
struct myStructA{int a; int b;};
struct myStructB{double c; };
struct myStructAny{/*???*/};/*???*/


VOID:
1
2
3
4
5
6
7
void Render(int aArg,int bArg, myStructAny struct){

if(typeid(struct) == typeid(myStructA)){/*cast_to myStructA*/}
else
if(typeid(struct) == typeid(myStructB)){/*cast_to myStructB*/}

}


CALL:
1
2
Render(1,1, myStructA); /*Choosed myStructA*/
Render(1,1, myStructB); /*Choosed myStructB*/

Inheritance?
closed account (GbRGwA7f)
@lastchance: Inheritance?
If possible no. I want to pass different irrelevant structs.
I confess I have no idea how to use this, but ...
#include <any>
#include <iostream>

void render( std::any a )
{
   std::cout << a.type().name() << '\n';
}

struct A{ int a, b; };
struct B{ double c; };

int main()
{
   A a;
   B b;
   render( a );
   render( b );
}
Last edited on
well, interestingly, we just had a discussion about substructing.

struct a {int type; int a, b;}; //type = 1 or something, enum seems ok?
struct b {int type; double d;}; //type = 2,
...
struct z {int type, ...};

void render (a* ap)
{
if(*ap.type == 2)
{
b* bp = (b*)(ap);
cout << *bp.d;
}
}

no matter what type it is, type is accessible from any of them as the first element, due to alignment etc, so you can get the type and then handle it internally.

there are other ways to do the same thing -- eg basically taking an axe to the problem and treating the structs as byte soup. So yes, it is possible. Should you do this? Probably not. Its a mad hax answer to a 'can you do it' question --- which begs "what do you really want to accomplish that can't be done with inheritance or polymorphism?" Because this works, but its ugly, and its not a good way to do anything.
Last edited on
Use function overloading?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <string_view>
#include <iostream>

struct a { int    x; };
struct b { double y; };

void write(std::string_view label, auto const& data) { std::cout << label << data   << '\n'; }
void write(std::string_view label, a data)           { std::cout << label << data.x << '\n'; }
void write(std::string_view label, b data)           { std::cout << label << data.y << '\n'; }

int main()
{
    a my_a{42}; 
    b my_b{24};
    
    write("a: ", my_a);
    write("b: ", my_b);
    write("const char[6]: ", "hello");
    write("double: ", 24.2);
}

Live demo:
https://godbolt.org/z/adx65v
Last edited on
closed account (GbRGwA7f)
Well, I did something like this and yes I had to use Inheritance sorry:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <typeinfo>
#include <string>

using namespace std;

struct MyStructBase
{
public:MyStructBase(){};
       virtual void test() {};
};

struct MyStructA :  MyStructBase
{
public:
    virtual void test() {};
    int a;
    MyStructA(int x) :a(x) {};
};

struct MyStructB : MyStructBase
{
public:
    virtual void test() {};
    string stringValue;
    MyStructB(string x) :stringValue(x) {};
};

template<typename T>
auto Render(T mystruct)
{
    return mystruct;
}

int main()
{
    auto testStruct1 = Render(MyStructA(192));
    auto testStruct2= Render(MyStructB("test"));

    if(typeid( testStruct1) == typeid(MyStructA))
    {
       string strOut1 = to_string(dynamic_cast<MyStructA*>(&testStruct1)->a);
       cout << "\n" << strOut1;
    }

    if (typeid(testStruct2) == typeid(MyStructB))
    {
        string strOut2 = dynamic_cast<MyStructB*>(&testStruct2)->stringValue;
        cout << "\n" << strOut2;
    }
}


OUT:
1
2
192
test


Thank you all for help.
Last edited on
Can you please explain why something like mbozzi's solution doesn't work for you? Simple function overloading looks like it will do everything you need. Your structs have completely different data in them, and you are not showing how they are related. There are many different designs that could possibly apply here, but it's hard to tell what you actually want. You might also want to consider constexpr if or template specialization, to at least avoid the unnecessary runtime checks.

That last code uses all the bad parts of dynamic behavior and inheritance and none of the good parts. Your Render functions don't even do anything now. And what's up with the test() function?

Instead of using typeid/dynamic_cast, use virtual functions and polymorphism. It would at least be better than what you have now.

If your structs had something common between them, you could also do something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>

struct myStructA {
    int a;
    int b;
    void Render()
    {
        std::cout << "a: " << a << ", b: " << b << '\n';
    }
};
struct myStructB {
    double c;
    void Render()
    {
        std::cout << "c: " << c << '\n';
    }
};

template <typename MyStruct>
void Render(int patties, int buns, MyStruct myStruct) {
    // not sure what you want to use your first two parameters for
    std::cout << "patties: " << patties << ", buns: " << buns << '\n';
    myStruct.Render();
}

int main()
{
    myStructA structA {};
    myStructB structB {};
    Render(1,1, structA);
    Render(1,1, structB);
}
Last edited on
closed account (GbRGwA7f)
@Ganado
Can you please explain why something like mbozzi's solution doesn't work for you?


I forgot to add. I also pass different class types. Let me explain it. I changed the struct base class to:

1
2
3
4
5
6
class MyBaseClass {
public:
MyStructBase();
virtual void test();

};


And the other types are in header lets say:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//works like enum but type is not enum
class Align : MyBaseClass
{
public:
      static const unsigned short int Middle=1;
      static const unsigned short int Top = 2;
      static const unsigned short int Left = 3;
      static const unsigned short int Bottom = 4;
      static const unsigned short int Right = 5;
};

class Orientation : MyBaseClass
{
public:

   unsigned short int Vertical = Align::Bottom;
   unsigned short int Horizontal = Align::Bottom;

    Orientation(unsigned short int, unsigned short int);

};


In cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/*SAME DEFINITIONS IN PREVIOUS*/
Orientation::Orientation(unsigned short  int _Vertical, unsigned short int _Horizontal) : Vertical(_Vertical), Horizontal(_Horizontal) {};

void resultToString(unsigned short int align) {
    switch (align)
    {
    case Align::Bottom: cout << "\nBottom"; break; case Align::Left: cout << "\nLeft"; break; case Align::Top: cout << "\nTop"; break; case Align::Right: cout << "\nRight"; break;
    default:
        break;
    }
}

int main()
{
    auto testStruct1 = Render(MyStructA(192)); //struct
    auto testStruct2 = Render(MyStructB("this is a test string"));//struct
    //not struct type is Orientation which has static "const unsigned short int" members
    Orientation* testStruct3 = Render(&Orientation(Align::Top, Align::Left));

    /*if(testStruct1...) ... if (testStruct2...)…*/

    if (typeid(*testStruct3) == typeid(Orientation))
    {
        Orientation* ori = dynamic_cast<Orientation*>(testStruct3);
        resultToString(ori->Horizontal);
        resultToString(ori->Vertical);
    }
/*…*/
}


OUT:
1
2
3
4
192
this is a test string
Horizontal->Left
Vertical->Top


I do not insist I should do it like this I can be wrong.
Last edited on
I still don't quite understand what's happening, but a few thoughts...

(1)
&Orientation(Align::Top, Align::Left)
is taking the address of a temporary object, which doesn't even compile for me.
main.cpp: In function ‘int main()’:
main.cpp:31:75: error: taking address of temporary [-fpermissive]
     Orientation* testStruct3 = Render(&Orientation(Align::Top, Align::Left));


(2)
1
2
3
4
class MyBaseClass {
public:
MyStructBase();
...
The name of what I assume is supposed to be your default constructor does not match your class name.

(3) As you mentioned, you are using your Align class as if it were just an enum. Why have it inherit from MyBaseClass if it has nothing to do with it?
You could just use an enum class instead,
1
2
3
4
5
6
7
8
enum class Align : short
{
      Middle = 1,
      Top    = 2,
      Left   = 3,
      Bottom = 4,
      Right  = 5
};

And pass in objects of type Align, not short.

(I also suggest just using int instead of short, unless you have a good reason for using shorts everywhere)
Last edited on
again, ¿what's wrong with mbozzi's solution?
¿what are you trying to do?

> not struct type is Orientation
¿ah?
1
2
3
4
5
6
7
void Render(int aArg,int bArg, myStructAny struct){

if(typeid(struct) == typeid(myStructA)){/*cast_to myStructA*/}
else
if(typeid(struct) == typeid(myStructB)){/*cast_to myStructB*/}

}

You're trying to fit a bad solution when there's a good one available:
1
2
myStructA::render(int a);
myStructB::render(int b);


If the structs are from a library or something else where you can't add render method then maybe create derived classes that have your render method.
Topic archived. No new replies allowed.