🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Any less ugly and more standard way of doing this?

Started by
49 comments, last by JoeJ 5 years ago

So basically I have pointers to classes that are members of another class. I know for sure they are members and I know their member names.  What I want to do is get a pointer to the class object that contains them. I messed around for a bit and came up with this, but it's ugly.  Anyone know of some better way, or should I just stick with this?

 


#include "pch.h"
#include <iostream>


/**
 #################################################################
 ##  
 #################################################################
 **/

class CClassA 
{

public:

   CClassA(int i) : m_iVal(i) {}

public:

   int m_iVal;

};

/**
 #################################################################
 ##  
 #################################################################
 **/

class CClassC1 : public CClassA

{

public:

   CClassC1() : CClassA(1) {}

};


//################################################################

class CClassC2 : public CClassA

{

public:

   CClassC2() : CClassA(2) {}

};

/**
 #################################################################
 ##  
 #################################################################
 **/

class COwner 

{

public:

   COwner() : m_iTestVal(999) {}

public:

   CClassC1 m_clData1;
   CClassC2 m_clData2;

   int m_iTestVal;


};

/**
 #################################################################
 ##  
 #################################################################
 **/

int main()
{
   COwner clOwner;

   CClassA *pC1 = &clOwner.m_clData1;
   CClassA *pC2 = &clOwner.m_clData2;

   COwner *pOnwer1 = (COwner *)((char *) pC1 - (size_t) &(((COwner *) nullptr)->*&COwner::m_clData1));
   COwner *pOnwer2 = (COwner *)((char *) pC1 - (size_t) &(((COwner *) nullptr)->*&COwner::m_clData2));

   std::cout << "pOnwer1->m_iTestVal       = " << pOnwer1->m_iTestVal << "\n"; 
   std::cout << "pOnwer2->m_iTestVal       = " << pOnwer1->m_iTestVal << "\n"; 

}

 

Advertisement

Looks fishy.

I assume you can't influence the "upper class" to simply pass along a pointer when the member is assigned ?

 

4 minutes ago, Green_Baron said:

Looks fishy.

I assume you can't influence the "upper class" to simply pass along a pointer when the member is assigned ?

 

It's more of a matter of space. I can do that except it's 8 bytes I have to store and there are 8 of these member classes in each owner class so that's 64 bytes.  There can be millions of these owner class objects so the space usage is very significant.

From this current thread, and this one, there is the impression that you are fighting against what C++ is.

C++ tries to enforce encapsulation that you seem to do all your best to destroy and this will undoubtedly lead to (weird) bugs (and probably other undefined behaviors).

So maybe you should ask yourself if you are using the right language. Another option is to find a better way to do it the right way. For example, if you don't have that much 'owners', then storing an uint8 to identify this 'owner' will lead to use few memory compared to store a pointer. You can also use std::map. You can also think again about your design and try to find a better way to solve your current problems.

Wow... that's..., really hard to read. If I got it right, you calculate the offset of each member and subtract it from the pointer address. I have to think about it (while I sleep :p) if I can figure out a better way of achieving what you want to do...

Only thing I could come up now is the following:

If you use C++17, and your class ends up having a size of 2^X, you could use alignas to align it in memory.  Then you just calculate the misalignment of the current pointer and correct it to the previous alignment boundary. Wouldn't be that much better I guess, but maybe a little bit.

However, _Silence_ made some good points.

 

Greetings

Almost looks like you're trying to (re)invent polymorphism.

Also, "clOwner"... Hmm....

🙂🙂🙂🙂🙂<←The tone posse, ready for action.

So leave it as it is then, LOL! .......... Actually I got rid of the *&. That was kind of a cut and paste artifact when I was playing with it.

OK here's the cleaner version using multiple inheritance........
 


// TestMuti.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "pch.h"
#include <iostream>

class COwner;


/**
 #################################################################
 ##  
 #################################################################
 **/

 class CClassA 
{

public:

   CClassA() : m_iSomeData(888) {}

public:

   int m_iSomeData;

};

/**
 #################################################################
 ##  
 #################################################################
 **/

class CClassC1 : public CClassA
{

public:

   CClassC1() : CClassA(), m_iSomeMoreData(101) {}

   COwner *GetOwner();

public:

   int m_iSomeMoreData;

};

//################################################################

class CClassC2 : public CClassA
{

public:

   CClassC2() : CClassA(), m_iSomeMoreData(102) {}

   COwner *GetOwner();

public:

   int m_iSomeMoreData;

};

/**
 #################################################################
 ##  
 #################################################################
 **/

class COwnerBase
{

public:

   COwnerBase() : m_iTestVal(999) {}


public:

   int m_iTestVal;

};

//################################################################

class COwner : public COwnerBase, public CClassC1, public CClassC2
{
 
public:

   COwner() {}

public:

};

/**
 #################################################################
 ##  
 #################################################################
 **/

COwner *CClassC1::GetOwner()
{
   return static_cast<COwner *>(this);
}

COwner *CClassC2::GetOwner()
{
   return static_cast<COwner *>(this);
}

//################################################################

int main()
{
   COwner clOwner;

   CClassC1 *pC1 = static_cast<CClassC1 *>(&clOwner);
   CClassC2 *pC2 = static_cast<CClassC2 *>(&clOwner);

   COwnerBase *pOnwer1 = pC1->GetOwner();
   COwnerBase *pOnwer2 = pC2->GetOwner();

   std::cout << "pOnwer1->m_iTestVal       = " << pOnwer1->m_iTestVal << "\n"; 
   std::cout << "pOnwer2->m_iTestVal       = " << pOnwer1->m_iTestVal << "\n"; 
}

 

And here's an updated version which let's you access your sub-objects as an array which is something I need .........


// TestMuti.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include "pch.h"
#include <iostream>

class COwner;


/**
 #################################################################
 ##  
 #################################################################
 **/

 class CClassA 
{

public:

   CClassA(int iVal) : m_iSomeData(iVal) {}

public:

   int m_iSomeData;

};

/**
 #################################################################
 ##  
 #################################################################
 **/

class CClassC1 : public CClassA
{

public:

   CClassC1() : CClassA(101), m_iSomeMoreData(101) {}

   COwner *GetOwner();

public:

   int m_iSomeMoreData;

};

//################################################################

class CClassC2 : public CClassA
{

public:

   CClassC2() : CClassA(102), m_iSomeMoreData(102) {}

   COwner *GetOwner();

public:

   int m_iSomeMoreData;

};

/**
 #################################################################
 ##  
 #################################################################
 **/

class COwnerBase
{

public:

   COwnerBase() : m_iTestVal(999) {}


public:

   int m_iTestVal;

};

//################################################################

class COwner : public COwnerBase, public CClassC1, public CClassC2
{

 
public:

   COwner() {}

   CClassA *GetChild(int iIdx)
   {
      return (CClassA *) ((char *) this + s_aOffset[iIdx]);
   }

public:

   static const size_t s_aOffset[2];

};

const size_t COwner::s_aOffset[2] = {
   (size_t) static_cast<CClassC1 *>((COwner *) 8) - 8,
   (size_t) static_cast<CClassC2 *>((COwner *) 8) - 8
};

/**
 #################################################################
 ##  
 #################################################################
 **/

COwner *CClassC1::GetOwner()
{
   return static_cast<COwner *>(this);
}

COwner *CClassC2::GetOwner()
{
   return static_cast<COwner *>(this);
}

//################################################################

int main()
{
   COwner clOwner;

   CClassC1 *pC1 = static_cast<CClassC1 *>(&clOwner);
   CClassC2 *pC2 = static_cast<CClassC2 *>(&clOwner);

   COwnerBase *pOnwer1 = pC1->GetOwner();
   COwnerBase *pOnwer2 = pC2->GetOwner();

   CClassA *pChild1 = clOwner.GetChild(0);
   CClassA *pChild2 = clOwner.GetChild(1);

   std::cout << "pOnwer1->m_iTestVal       = " << pOnwer1->m_iTestVal << "\n"; 
   std::cout << "pOnwer2->m_iTestVal       = " << pOnwer1->m_iTestVal << "\n"; 

   std::cout << "pChild1->m_iSomeData      = " << pChild1->m_iSomeData << "\n"; 
   std::cout << "pChild2->m_iSomeData      = " << pChild2->m_iSomeData << "\n"; 


}

You may be wondering what the 8 is for in "(size_t) static_cast<CClassC1 *>((COwner *) ? - 8" ..... With multiple inheritance 0 (i.e. a nullptr) is special. When doing casts C++ checks for it so it doesn't apply the offset you would need if it was a real pointer.  This is so nullptr always stays a nullptr.  In this case however I am trying to do an offset calculation so I want the compiler to change it when I do the cast. Therefor I have to use some other value other than 0 and 8 is a nice round number ?.  

8 hours ago, fleabay said:

Almost looks like you're trying to (re)invent polymorphism.

 

You are right. I would never have seen that :D - probably because I avoid polymorphism as hell ?

This topic is closed to new replies.

Advertisement