Any less ugly and more standard way of doing this?

Started by
48 comments, last by JoeJ 4 years, 9 months ago
12 minutes ago, JoeJ said:

Not sure if i get this - it keeps me puzzling how the body of declaration in upper post looks like.

I assume the classes like class C1, class C2, etc. inherit frome some base, and you only need to include this light weight base class declaration in the header, while in the cpp file you include the inherited stuff with all the code?

Would make sense to me :)

 

 

I don't think I have an 100% complete answer to your question but having a light-weight class template sounds like a good start.

Advertisement
10 minutes ago, JoeJ said:

Not sure if i get this - it keeps me puzzling how the body of declaration in upper post looks like.

I assume the classes like class C1, class C2, etc. inherit frome some base, and you only need to include this light weight base class declaration in the header, while in the cpp file you include the inherited stuff with all the code?

Would make sense to me :)

 

 

Yes, in this case C1 and C2 inherit from the same class and take the same parameters. I actually construct instances of them in the subdivide function, but actually for the purposes of my comment it doesn't matter.  I just used this particular explicit instantiation in the example because I had posted the function before, so you could see the syntax. 

I probably should have used a simple example. I was just trying to say you can often treat a template just like a regular class if you use explicit instantiation. You put the class definition in the .h and the code in the .cpp and it works as expected.  You can do this with any template class if explicit instantiation works for what you are trying to do.

23 hours ago, JoeJ said:

Usually i shy away from discussion about programming itself, but to me this thread sounds you're running into some kind of hassle because you are used to combine data and function into classes. 

Wouldn't this be easier if you had just one class to group functions that operate on tree data, instead one class per node?

This could go so far you have some classes without member data at all and just static member functions. This leads to longer parameter lists, but pointer arithmetic would become simple index math; data layout and memory management becomes independent from class logic; code might be easier to read and reuse ...and stuff like that.

You guys were right. Struct is 100% useful as a class.

What actually got me into this conversation was this. Most people, like you are suggesting, use classes(or structs) to make data trees, data components' structure, etc.

But what regards me, you just can't use a class(or struct) and then not make an object out of it(for object-oriented programming).

This might not be true for template classes(or structs) and C++ is a multi-paradigm language.

But from what I learned from Bjarne Stroustrup's book(C++ Programming Language, 4th edition), who actually invented the C++ and worked with others on the 2011 standard:
All four paradigms of C++ language are used to combine their aspects(in this case, an aspect would mean a part of a paradigm).

Even if you are using generic programming paradigm of C++, OOP paradigm is no less important.

So if I got it completely correctly, this is what classes and structs are used for: making real-life objects.

https://www.learncpp.com/cpp-tutorial/81-welcome-to-object-oriented-programming/

Like I've said before, you guys were right: classes are the same as structs.

What you didn't notice (probably my fault) was this is just my personal opinion.

Here is a chapter section of a very good C++ tutorial, where the author unintentionally compares the class and struct usage(I think):

https://www.learncpp.com/cpp-tutorial/82-classes-and-class-members/

My opinion is based on the fact classes came after structs, the latter were used in C. Also when it comes to collaborating with other programmers in a team(which I have done, am doing and have a lot of experience in):

You just can't use a mixture of structs and classes without making some really ugly code. So I think it would be a lot better to just standardize the usage of class or struct into a convention where only one or the other can be used- by the next(or later) ISO standard(as in a selected dominating word) or at least per-project.

(And no, I am not going to send this info to ISO C++ standard committee :D).

 

54 minutes ago, Gnollrunner said:

explicit instantiation

To be honest, sounds like alien programmer jargon to me, haha :)

But i did read about it, and finally i got it to work:


//diffusion.cpp:

#include "MeanValueWeights.h"

template <typename T>
void Diffusion::Diffuse2 (std::vector<T> &out,
    const std::vector<T> &in, const float duration, 
    const MeshConnectivity &conn, const CotanWeights &weights, const MeanValueWeights &TESTweights, 
    const int maxIterations)
{

// implementation code, including using the MeanValueWeights class which has only forward declaration in diffusion.h

}

//explicit instantiation:

template void Diffusion::Diffuse2<float> (std::vector<float> &out,
            const std::vector<float> &in, const float duration, 
            const MeshConnectivity &conn, const CotanWeights &weights, const MeanValueWeights &TESTweights, 
            const int maxIterations);

template void Diffusion::Diffuse2<vec> (std::vector<vec> &out,
			const std::vector<vec> &in, const float duration, 
			const MeshConnectivity &conn, const CotanWeights &weights, const MeanValueWeights &TESTweights, 
			const int maxIterations);

And yes i can call it from other cpp files :D And i get how this gives the compiler what he needs to know :)

Awesome! Thanks man, no more issues with templates!

 

 

 

 

 

 

 

1 hour ago, Acosix said:

But what regards me, you just can't use a class(or struct) and then not make an object out of it(for object-oriented programming).

I do this a lot, for example:


class AnswerDifficultQuestions

{

public:

static int CalcMeaningOfLife ()

{

return RocketScience ();

}

private:

static int RocketScience ()

{

return 42;

}
  
};

There is no member data, only static functions and so no reason to ever make an object from this class.

The only reason to use class here is to group functions that belong to a certain purpose, and to hide implementation details. If there are no details to hide i would use namespace instead class.

I don't think this is related to paradigms, i just use it to keep stuff organized.

My proposal to separate data from functions surely goes beyond that, but i don't know if this means a paradigm shift from OOP to something else like DOD or whatever. I do not really know what all those words mean - one reason i shy away from discussion about programming :)

 

 

 

10 hours ago, JoeJ said:

And yes i can call it from other cpp files :D And i get how this gives the compiler what he needs to know :)

Awesome! Thanks man, no more issues with templates!

Cool! At least that's one useful thing that came out of this thread ?

Edit: Oh, and I see I made 300. Does that make me a Spartan now?

1 hour ago, Gnollrunner said:

Cool! At least that's one useful thing that came out of this thread ?

Edit: Oh, and I see I made 300. Does that make me a Spartan now?

Yes. But now you are at 301 and no Spartan any more ?

 

11 hours ago, JoeJ said:

There is no member data, only static functions and so no reason to ever make an object from this class.

That's a good point. I also use this technique a lot to make functions which are not meant to be used directly inaccessible. Only the classes and functions that should use it are declared as a friend.

 

Greetings

On 6/27/2019 at 10:48 PM, Gnollrunner said:
On 6/27/2019 at 10:02 PM, 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.

This is still the post which calls my attention.

Using different classes still smells to me. But it's not just a matter of taste or programming paradigms - it's the hope you could reduce complexity maybe.

For example, assuming you have N classes to handle all prism cases, could you use just one or two instead N to handle them all? Using some bit flags per node, maybe ending up with less code at least, while the number of branches remains similar to what you have now including the inheritance logic done by the language?

I assume the whole indexing thing would become a lot easier as well if this makes sense. (that's what i meant initially)

 

But what confuses me the most is: Why do you need a owner class for the children? Couldn't the parent handle this, reducing complexity again?

Pretty sure you have your reasons (like saving the memory of owner data for the leafs), but that's the questions that come up making our thoughts slip i guess.

One thouht that often makes me see potential improvements with trees is having the parent to process all it's children instead processing each child in isolation.

 

 

 

 

1 hour ago, JoeJ said:

This is still the post which calls my attention.

Using different classes still smells to me. But it's not just a matter of taste or programming paradigms - it's the hope you could reduce complexity maybe.

I could use one class. I could put flags and ifs in and do check throughout the routine.  That's perhaps what many people would do. However by calling a separate function I make all those little decisions at the top in one virtual function call. The program is doing less work for the same result at the expense of more destination code (not really so much source code) .  But then these days code is cheap compared to data.  Here's what one of my classes looks like
 


class CDLSpherePrismViewDataUpBW2C3 : public CDLSpherePrismViewDataUpBW2
{
public:

   CDLSpherePrismViewDataUpBW2C3( CDLTerrainObject *pObj, CDLSpherePrismViewData *pParent, IDLChildIndex iCI, CDLObjectNode *pCenter, FDLCoord fRad,
      CDLSphereFaceTriView *pFloor, CDLSphereFaceTriView *pCeiling,
      CDLSphereFaceQuadView *pWall1, CDLSphereFaceQuadView *pWall2, CDLSphereFaceQuadView *pWall3) : 
      CDLSpherePrismViewDataUpBW2(pObj,pParent,iCI,pCenter,fRad,pFloor,pCeiling,pWall1,pWall2,pWall3)
   {
      this->m_clCA.SetPrismUpChild3(pParent->m_clCA);
   }

   int GetChildID() override { return 2; }
};

static_assert(sizeof(CDLSpherePrismViewDataUpBW2C3) == sizeof(CDLSpherePrismViewData), "CDLSpherePrismViewDataUpBW2C3 size mismatch!");

And that essentially inherits from this  :


typedef TDLSpherePrismData<CDLSpherePrismViewUpData, CDLSpherePrismViewData,
   CDLSpherePrismViewDataUpFW2C1, CDLSpherePrismViewDataUpBW2C2, CDLSpherePrismViewDataUpBW2C3, CDLSpherePrismViewDataDnBW2C4,
   CDLSpherePrismViewDataUpFW2C5, CDLSpherePrismViewDataUpBW2C6, CDLSpherePrismViewUpDataBW2C7, CDLSpherePrismViewDataDnBW2C8, 
   CDLSphereFaceTriViewUpCW2    , CDLSphereFaceTriViewUpCC2    , CDLSphereFaceTriViewDnCC2    , CDLSphereFaceTriView         , 
   CDLSphereFaceQuadView        , EDLFaceSides::_FBFFB_        , EDLFaceSides::_FBFBB_        , EDLFaceSides::_FBFBB_        , 
   0> TDLSpherePrismViewDataUpBW2;

Almost all differences in the class are handheld by the temples.  Call it a performance hack....

Quote

For example, assuming you have N classes to handle all prism cases, could you use just one or two instead N to handle them all? Using some bit flags per node, maybe ending up with less code at least, while the number of branches remains similar to what you have now including the inheritance logic done by the language?

Yes I could .... but again, the program would have to check for all those bit flags when the code is running and do different things based on them. That's extra work and would slow it down.  My programming style is straight though code as much as possible... Try to eliminate as many run-time checks as you can. It's analogous to taking the if out of the loop and having two loops instead. It may be more code, but it should run faster.

 

Quote

I assume the whole indexing thing would become a lot easier as well if this makes sense. (that's what i meant initially)

It might, but I don't try to make my life easier. I try to make the computer's life easier.  What I've settled on now is create a class object (kind of a container) with a big buffer in the middle and use placement new to populate it with the 8 octree nodes.  As I said earlier I'm using a 2 byte offset in each octree node that will get me to it's container.

In general sometimes I come up with new ways (at least  new for me) to handle things and in if they work out, I've added a new weapon to my arsenal that I can use again and again. Once you figure it out, it's not so complex.

 

Quote

But what confuses me the most is: Why do you need a owner class for the children? Couldn't the parent handle this, reducing complexity again?

Well for example, I want to have a back pointer from the children to the parent. I can put it in each child but but that's 8 bytes each. But the two byte offset I'm using to get to the container is packed in with a bunch of other flags so it's basically free. Now I can put the back pointer in the container because it's shared by all children and I'm only using 8 bytes instead of 64.

Furthermore allocated objects need a reference count since they can be referenced outside of the tree, and I need to make sure I'm not deleting a node when the tree is being reorganized, because that node (i.e. voxel) may be being used by a mesh.  There is also other information in the header for the memory allocation as is usually the case in most heap systems. 

However it turns out because of the algorithm this is a very temporary situation. If a node dies it's siblings will die in the next iteration. So putting them together in memory make sense because it's 1 memory header as opposed to 8, and there is also a memory cache advantage when things are together in memory.  So again that's a big memory savings.

Ok, i'm convinced. :)

But i can't help making pointer arithmetric looking elegant or something. I'd say making it work is all you can aim for in such a situation. Whenever i did something similar, it always was ugly. But who cares if it's hidden in a helper function.

This topic is closed to new replies.

Advertisement