Sign in to follow this  
Mufflot

Hiding interfaces from the user. C++

Recommended Posts

In my current project I don't want a user to be able to create an object, which is handled internally by some other class. F.ex. if I have this Time class, which is created when the engine is created, I want to hide the constructor. My current code is like this:


class Time
{
public:
float GetDeltaTime();
...

private:
Time();
...

friend class Engine;
}



By going with this method, I declare friendships everywhere. I'm wondering, can this be done in some way without friendships?

Mufflot

Share this post


Link to post
Share on other sites
You can hide/prevent the object creation/deleting from user for example by creating interface for your Time class like this:


// Time interface.
class Time
{
protected:
// protected destructor prevents the user from deleting your time object.
virtual ~Time()
{
}
public:
virtual float GetDeltaTime() = 0;
};

// Engine interface.
class Engine
{
public:
virtual ~Engine()
{
}

virtual Time* GetTime() = 0;
};




In somewhere you have implemented the Time and Engine interfaces like this:


// Class which implements the time interface.
class TimeImplementation: public Time
{
public:
TimeImplementation()
{
}

virtual ~TimeImplementation()
{
}

virtual float GetDeltaTime()
{
// TODO: Implement!
return 1.0f;
}
};

// Class which implements the engine interface.
class EngineImplementation: public Engine
{
public:
EngineImplementation()
{
}

virtual ~EngineImplementation()
{
}

virtual Time* GetTime()
{
return &iTime;
}
private:
TimeImplementation iTime;
};

// Function which creates the engine.
Engine* CreateEngine()
{
return new EngineImplementation;
}



Here is an example code for creating and using the engine.


void example()
{
Engine* engine = CreateEngine();

Time* time = engine->GetTime();
float deltaTime = time->GetDeltaTime();

delete engine;
}



Share this post


Link to post
Share on other sites
There is the pimpl-idiom, where the implementation is only visible to internal places, and the interface would have a pointer to that forward declared implementation. This could help you emulate .net's internal visibility.

[nitpick: your class declaration is missing a semicolon]

edit: s/impl/pimpl/

Share this post


Link to post
Share on other sites
I would just put the headers in a directory called "internal" or "private" or something like that. It communicates clearly that the classes should not be used outside the API and it doesn't clutter the code.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mufflot
Thanks for the replies!

I'm going to try out ttstreamer's solution, it looked quite nice.


So how do you prevent users from creating an instance of TimeImplementation?

Share this post


Link to post
Share on other sites
jonathanjansson: Haha yeah I actually missed the part where XImplementation's constructor was public ;).

SiCrane: Sounds like a good idea, any tips on how can you do that? I'm not at my home so I can't really try it out by myself atm.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mufflot
SiCrane: Sounds like a good idea, any tips on how can you do that? I'm not at my home so I can't really try it out by myself atm.


The post right in the middle between your "I'm wondering" and "Thanks for the replies!" contains a clue, as does this post.

pimpl

Share this post


Link to post
Share on other sites
The first question you should ask yourself when trying to hide something from the user isn't how, but why. Is there any good reason your user shouldn't be able to create their own Time instances? And by "good reason" I mean will it break your engine in any way if a second one is created, or are you just doing this out of a sense of "you should only need one of these."

If it won't break your engine to create a second Time instance*, then don't bother hiding it from the user. They can access your engine's instance if they want to, or maybe they'll think of something you haven't and will want to make a second one for some reason. Generally speaking, the only good reason to hide something from the user is if it has the potential to break things and you need to limit access in a way that avoids that potential**.

-----
* In such a circumstance you should be using a singleton design anyway, or even better, changing the design so that this won't be an issue.

** Good example: for classes that expect to be managed by a shared_ptr or intrusive_ptr or such, you might decide to use a private constructor with the named constructor idiom which creates them inside the appropriate smart pointer so the user can't forget to put them in one.

Share this post


Link to post
Share on other sites
I'd just use a static bool in the constructor, and an assertion. It isn't thread safe (volatile can help depending on your compiler):

class Time : noncopyable
{
public:
Time() // Don't call me!
{
static bool created = false;
assert(!created);
created = true;
}

// ...
};


This way you can relax the restriction easily in future by simply removing those lines. Your design isn't bent around a single Time instance.

It won't detect additional Time constructions during compilation, but by the time your code is tested you should be sure no additional instances are created.

Share this post


Link to post
Share on other sites
phresnel: Yeah haha, I checked out that pimpl-idiom, though pretty quick, and I missed the part where they declared the implementation in the .cpp file... thanks again. :)

Shinkage: It's just not complete objects, I want to hide certain member functions aswell. For instance, my Renderer class has this Initialize() function, which shouldn't be called more then once, since the program will leak memory then. But yeah I understand what you mean. I was mostly just curious in what ways you could do it. Now I got some ideas and I will see what I'll do.

rip-off: Ah yeah, would work aswell.

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

Sign in to follow this