• Advertisement
Sign in to follow this  

Personal opinions about how to handle common scenario and technical C++ question.

This topic is 3318 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, I have a couple of questions. Question 1) Ok, so I have a template base class (Octree in this case) that takes three template arguments: Entity, Octant, and Frustum. The class also has a virtual ADT method called int Collides(const Frustum*, const Entity*) = 0; Sample source code is as follows:
[source lang = "C++"]

// Octree module

template
<
typename Entity,
class    Octant,
class    Frustum
>
class
Octree
{
public:
     // Public methods here etc.

protected:
     virtual int Collides(const Frustum*, const Entity*) = 0
};


Now, I need to inherit from this class to implement its ADT method. I also need to provide an Octant class to pass to it as its Octant parameter. Sample source code is as follows:
[source lang = "C++"]

// JOctree module

// Entity/Frustum defined elsewhere

class JOctant
{
public:
     // Methods/data etc.
};

class JOctree : public Octree<Entity, JOctant, Frustum>
{
public:
     // Methods/data etc.
};

My question concerns this ... It is my understanding that if you include a file you are exposing its functionality to the including file. In the above scenario when I include JOctree I do want to expose the JOctree class so as to use it, but it becomes clutter that the class Octant is in the header - I do not want to expose JOctant at all as JOctant is used only by JOctree. JOctant is used entirely by JOctree. I cannot make JOctant a member of JOctree though since Octree requires it as a template argument (it is needed to be known at the definition of JOctree). It is important to have clean interfaces to your modules but exposing JOctant does not seem clean. I have heard that both the static keyword and unnamed namespaces are both looked down upon in headers. Any thoughts/help on how to expose ONLY WHAT YOU NEED from the innerworkings of headers? Question 2) Ok, so sometimes you have simple functions to directly manipulate members, i.e.
[source lang = "C++"]
//Version 1
int  GetType()const;
void SetType()const;

//Version 2
int& GetType()const;

In my experience it is always good to have consistent code (things are handled the same uniformly) so one should stick with either version 1 or version 2 and be consistent. I do not like version 1 because it is redundant compared with version 2 (code bloat) but I do not like version 2 because sometimes you do not want to expose your variables directly. Version two is not consistently going to work for you as well because at times Getting/Setting your variables may require different code than pure access. It seems like you could have a three way strategy : Get/Set/Access prepending your methods to outline their nature. Any thoughts? That's it for now. Thank you for your help. Jeremy

Share this post


Link to post
Share on other sites
Advertisement
1) If you have a class Derived that inherits from class Base, you need to include the header file for Base in the derived class header file. In other words, the compiler needs to know about the base type when you declare the derived type.

When you put JOctree in the template parameter list for the base type, it becomes part of that base type, and the compiler needs to know about regardless of whether or not you want anyone including the header file to know about it. Going back to the first example, someone could include the Derived header file and also get the declaration for Base, even if you never intended for the user to know about Base. Templates are one of those special cases where you can't really separate the interface from the implementation, so while it certainly isn't ideal, that's the way it is :/

2) If you're going to use getters and setters, then stick with version 1. Version 2 is rather pointless, since if you were going to expose a function that gives you direct access to a member variable, you might as well just have made that member variable public, cut down on redundant code, and avoided ugly syntax like Get_Value() = 5.

Share this post


Link to post
Share on other sites
Quote:
Original post by grill8
Question 1)

[8< --- ]

My question concerns this ...

It is my understanding that if you include a file you are exposing its functionality to the including file. In the above scenario when I include JOctree I do want to expose the JOctree class so as to use it, but it becomes clutter that the class Octant is in the header - I do not want to expose JOctant at all as JOctant is used only by JOctree. JOctant is used entirely by JOctree. I cannot make JOctant a member of JOctree though since Octree requires it as a template argument (it is needed to be known at the definition of JOctree).

It is important to have clean interfaces to your modules but exposing JOctant does not seem clean.


A common thing to do is use a namespace for implementation details. For example the boost libraries often use a "detail" namespace inside which things like JOctant will live. You can also pull JOctant in to its own header and put it in a subdirectory containing only code that are implementation details.

Quote:

I have heard that both the static keyword and unnamed namespaces are both looked down upon in headers.


They create duplicate objects and definitions. static definitions and anonymous namespaces are a dead-end here, IMHO.

Quote:

Question 2)

In my experience it is always good to have consistent code (things are handled the same uniformly) so one should stick with either version 1 or version 2 and be consistent.

I do not like version 1 because it is redundant compared with version 2 (code bloat) but I do not like version 2 because sometimes you do not want to expose your variables directly. Version two is not consistently going to work for you as well because at times Getting/Setting your variables may require different code than pure access.


Get()/Set() methods don't (often) provide much in the way behavior so you'll often hear people recommend that you avoid them if possible.

Ask yourself *why* you'd need to Get() the object member. If it's to perform some task X, would X be better written as a member function of the class so as not to expose the member directly and keep the implementation separate?

Sometimes it really is better to have Get()/Set() methods, but often there's a better way. For example, in the context of a generic tree container, you may think that you need Get()/Set() on the nodes. But consider that std::map and std::set are commonly implemented as RB trees. There are no Get()/Set() methods. Access is provided through iterators (and operator[] for the case of a map).

These STL classes use a comparison to slot nodes in to the correct place. A reasonably generic octree implementation could similarly use a "three way" comparison to slot a node in to a tree. On the other hand, this may be entirely inappropriate for your needs, but you see what I'm getting at, hopefully.

If you do need "direct" access in some way, Get()/Set() methods are more idiomatic than having a single function that returns a reference, IMHO.

Share this post


Link to post
Share on other sites
Quote:
Original post by grill8
Any thoughts/help on how to expose ONLY WHAT YOU NEED from the innerworkings of headers?
It's just one of those things you have to put up with in C++. It is common to use a 'detail' namespace, all things in the detail namespace are for implementation purposes only. This doesn't necessarily hide things, but at worst it flags to users which bits are the library's interface, and which bits are not.

Quote:
Original post by grill8
It seems like you could have a three way strategy : Get/Set/Access prepending your methods to outline their nature.
Usually none of those are good options, they're just not needed that often. When I do use accessors and mutators, I go for version 1. If you end up doing it a lot then such things, particularly version 2, tend to imply that the design is suboptimal and the data is better off elsewhere; they are 'code smells' that hint towards broken encapsulation and violations of the Law of Demeter.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement