mardi 31 mars 2015

Hiding members in public interface and ODR

I have multiple classes in a library that have internals that I wish to hide from client code. From the client's perspective, each class is queried from a library class and is only used as an opaque pointer. An example is as follows:



struct SomeSystem;
void doSomethingToSomeSystem(SomeSystem* system, Parameters params);
void doSomethingElseToSomeSystem(SomeSystem* system, Parameters params);


On the implementation side, SomeSystem has multiple members which are not visible to the caller. This is all fine but I don't really like the clunky usage syntax:



SomeSystem* system = lib->getSomeSystem();
doSomethingToSomeSystem(system, params);
doSomethingElseToSomeSystem(system, params);


Another approach is this:



struct SomeSystem;
namespace somesystem {
void doSomething(SomeSystem* system, Parameters params);
void doSomethingElse(SomeSystem* system, Parameters params);
}


With the usage code:



SomeSystem* system = lib->getSomeSystem();
somesystem::doSomething(system, params);
somesystem::doSomethingElse(system, params);


I could also use global methods called doSomething and doSomethingElse and depend on function overloading if another type also defines doSomething. However, in this case, it is hard to find all "members" of SomeSystem in an IDE.


I am tempted to actually use member functions:



struct SomeSystem {
void doSomething(Parameters params);
void doSomethingElse(Parameters params);
};


With the usage code:



SomeSystem* system = lib->getSomeSystem();
system->doSomething(params);
system->doSomethingElse(params);


The last snippet looks good to me but SomeSystem is no longer an opaque pointer - it actually defines members. I'm a bit wary of this. One potential problem is the one definition rule. However, the "public" definition and "private" definition of the class will only be visible to different translation units. Is there any other badness hidden here? If the client code tries to instantiate SomeSystem on the stack or using new it would obviously crash the program. But I'm willing to accept that. Perhaps I can get around this by providing a private constructor in the public interface.


Another approach is of course to define an abstract class with pure virtual methods. However, I would like to avoid the overhead of this in case it is not absolutely necessary.


Aucun commentaire:

Enregistrer un commentaire