Jump to content
  • Advertisement
Gnollrunner

C++ Any less ugly and more standard way of doing this?

Recommended Posts

Advertisement
Quote

 

    COwner *pOnwer2 = (COwner *)((char *)pC1 - (size_t) &(((COwner *) nullptr)->*&COwner::m_clData2));
Can anyone tell me what does above snnipt do like in  "pC1 - (size_t)" the  "-"  between is it - if it is then why we are subtracting it...

Share this post


Link to post
Share on other sites
45 minutes ago, syedMohib44 said:

COwner *pOnwer2 = (COwner *)((char *)pC1 - (size_t) &(((COwner *) nullptr)->*&COwner::m_clData2));
Can anyone tell me what does above snnipt do like in  "pC1 - (size_t)" the  "-"  between is it - if it is then why we are subtracting it...

 

He is using pointer arithmetics.

This piece

(size_t) &(((COwner *) nullptr)->*&COwner::m_clData2))

calculates the byte difference in memory between a COwner and its member m_clData2. If you have the address (pointer) of a CClassC2 member, you can get the address of its owner by subtracting that distance from it. All you need to do then is to cast the char* to a COwner*.

 

Greetings

Share this post


Link to post
Share on other sites
3 hours ago, DerTroll said:

He is using pointer arithmetics.

This piece


(size_t) &(((COwner *) nullptr)->*&COwner::m_clData2))

calculates the byte difference in memory between a COwner and its member m_clData2. If you have the address (pointer) of a CClassC2 member, you can get the address of its owner by subtracting that distance from it. All you need to do then is to cast the char* to a COwner*.

 

Greetings

Thank you dude it helped me.

Share this post


Link to post
Share on other sites
8 hours ago, DerTroll said:

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

Trust me, I would never spend any trying to reinvent polymorphism.  I'm actually spending the effort to try to save memory.  My planet's terrain is up around 2 gig at max resolution and I want to cut it back some. Also this should improve the speed somewhat as I'm doing one allocation vs eight.

Quote

I'm curious about your current usecase for this

This is for an octree of voxels. In my particular case the nodes always have either no children or all eight. So therefore it makes sense  to put the 8 children together and save 7 pointers, not to mention 7 memory headers with reference counts and other information that's common to all eight. On the other hand I need to be able to reference a single voxel sometimes and when I do that it may need the common information that's shared by all it's siblings. For instance one of my mesh formats is a a list of voxels, each voxel holding a list of triangles. It's done this way because voxels get put into chunks. Each chunk has two meshing a border mesh and a center mesh. The border mesh may need to change if a neighboring chunk changes. However by using this one level if indirection, I just rebuild the border mesh by generating new triangle in each voxel included in the border mesh voxel list.

I also generate lists of individual voxels when a player moves a around in order to collision.  So basically sometimes it makes sense to treat them as individuals but for the octree it makes sense to generate them and store them together. Of course I could do this in a more standard way but it would take more memory and probably be a bit slower, however like I said, I'm mainly trying to keep the memory usage down.

And now for my next abomination :D ........

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

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

class COwner;

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

class COwnerBase
{

public:

   COwnerBase() : m_iTestVal(999) {}


public:

   int m_iTestVal;

};

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

class CClassA 
{

public:

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

public:

   int m_iSomeData;

};

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

template<intptr_t POWN> class CClassC1 : public CClassA
{

public:

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

   COwnerBase *GetOwner() 
   {
      return (COwnerBase *)((char *) this + POWN);
   }

public:

   int m_iSomeMoreData;

};

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

template<intptr_t POWN> class CClassC2: public CClassA
{

public:

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

   COwnerBase *GetOwner() 
   {
      return (COwnerBase *)((char *) this + POWN);
   }

public:

   int m_iSomeMoreData;

};

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

class COwnerTemplate : public COwnerBase
{

public:

   COwnerTemplate() {}

   CClassC1<0> m_clC1;
   CClassC2<0> m_clC2;

};

constexpr intptr_t IC1OFF = - (intptr_t) offsetof(COwnerTemplate,m_clC1);
constexpr intptr_t IC2OFF = - (intptr_t) offsetof(COwnerTemplate,m_clC2);

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

class COwner : public COwnerBase
{

public:

   COwner() {}

   CClassC1<IC1OFF> m_clC1;
   CClassC2<IC2OFF> m_clC2;

   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] = {
   offsetof(COwner,m_clC1),
   offsetof(COwner,m_clC2)
};

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

int main()
{
   COwner clOwner;

   CClassC1<IC1OFF> *pC1 = &clOwner.m_clC1;
   CClassC2<IC2OFF> *pC2 = &clOwner.m_clC2;

   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"; 

}

This one uses templates to pass in the member location in the owner to the member itself. It's a bit more versatile because I don't need to know the exact implementation of the holding class. The members only need to see a base or the holding class. On the other hand if you take a look at it, it *IS* a real abomination 😄 I construct fake class just to get the offsets of members and then use those when building the real class.  It was kind of a chicken and egg problem. You can't know the offset of the members without building the class first. But you need the offsets to build the class. Also I found out about the "offsetof" operator which made my life a bit easier.

Share this post


Link to post
Share on other sites
36 minutes ago, Magogan said:

Or, you know, you could have just allocated an array of children.

Won't work in this case.  Each child is a separate class. It has the same data but the v-table is different to optimize when subdividing.  There are some differences like which way the voxel walls are facing and the orientation. These are prism voxels not cubes so when you subdivide into an occurred two of the voxels have different orientation.  They are also wrapped  around a sphere which means the orientation of walls will vary in places.

Also that doesn't fix the primary problem of having voxels efficiently access data that are shared with it siblings. That issues the same whether you use an array or not.

Share this post


Link to post
Share on other sites
Posted (edited)

Holy potatoes, complicated.

Ever thought of better named classes as objects or using smart pointers...?

Edited by Acosix

Share this post


Link to post
Share on other sites
28 minutes ago, Acosix said:

Holy potatoes, complicated.

Ever thought of better named classes as objects or using smart pointers...?

Yes it's kind of complicated, but on the other hand I'm pretty happy with the performance, especially given I'm using an 8 year old computer, and it should get better as I make some improvements.

I'm not sure what you mean by better named classes. The code I posted is just test code, for this particular optimization I'm adding. As for smart pointers, I already have my own smart pointer system which is incorporated into a special heap implementation.

Share this post


Link to post
Share on other sites

Your game world uses 2GB of RAM?

Which would be kind of a lot of it.

About class naming. Use /* */ at the start of the code file. Describe what is each class used for.

Another naming convention is to use // comments before each class and describe per-class what is it used for.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!