|
From: blurk on 21 Jan 2008 18:16 I'm in a situation where I am defining a C++ API representing an interface such that different applications can use the interface, yet there can be different libraries that implement the interface. The goal is for the applications to be able to be run against a test library implementing the interface as well as the real implementation library itself. The way I have approached the API design is to specify a bunch of abstract base classes, and obviously only the types of these base classes are known to the API. The applications do not need to know anything about the implementation classes (I am maybe somewhat overzealous about that). For various reasons, I want to avoid the Pimpl idiom (one target for the application is embedded environments, and I don't want a bunch of delegating member functions or to fragment the construction of the objects). Also, for any given implementation, most of the abstract base classes will only have exactly one implementation subclass. I have come up with a way to implement getting a pointer or reference of the derived class type from a pointer of the base class type without using a dynamic_cast, and I'm curious about (a) whether this method can be improved and (b) whether I'm seriously deluded about doing this (i.e. am I taking my anti-cast stance too far). One "justification" I have for avoiding dynamic_cast here is that my approach here has a stronger guarantee than can be provided by dynamic_cast because dynamic_cast has the possibility of returning a null pointer because the types do not match up. So here is the general outline of how I implement this: Interface.h: namespace Interface { // I use a base class using the curiously recurring template pattern // because I need each abstract class to have a unique nested class // providing an anonymous type that I can use in return values. template <typename T> class Base { public: class I; virtual I &Get() = 0; virtual const I &Get() const = 0; }; class Abstract : public Base<Abstract> { public: virtual void Foo() const = 0; }; } // namespace Interface Implementation.h: namespace Implementation { class Derived; } namespace Interface { template <> class Base<Abstract>::I : public Abstract { public: // Accessors returning nested type which is undefined to the // interface, but visible to implementation virtual I &Get() { return *this; } virtual const I &Get() const { return *this; } // Conversion operators to get to the actual implementation // type. virtual operator Implementation::Derived &() = 0; virtual operator const Implementation::Derived &() const = 0; }; } // namespace Interface namespace Implementation { class Derived : public Interface::Base<Interface::Abstract>::I { public: // Only here do we know the actual implementation type virtual operator Derived &() { return *this; } virtual operator const Derived &() const { return *this; } virtual void Foo() const { std::cout << "Derived::Do" << std::endl; } void Impl() const { std::cout << "Derived::Impl" << std::endl; } }; } //namespace Implementation Implementation.cpp: int main(int argc, char *argv[]) { Implementation::Derived d; Interface::Abstract *ap = &d; Implementation::Derived &rd = ap->Foo(); rd.Do(); rd.Impl(); const Interface::Abstract *cap = ap; const Implementation::Derived &crd = cap->Foo(); crd.Do(); crd.Impl(); return 0; } -- Bernie I do not want email replies, please followup to the group. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Sebastian Redl on 22 Jan 2008 19:16 blurk wrote: > I'm in a situation where I am defining a C++ API representing an > interface such that different applications can use the interface, yet > there can be different libraries that implement the interface. The goal > is for the applications to be able to be run against a test library > implementing the interface as well as the real implementation library > itself. > > The way I have approached the API design is to specify a bunch of > abstract base classes, and obviously only the types of these base > classes are known to the API. The applications do not need to know > anything about the implementation classes (I am maybe somewhat > overzealous about that). No, that's an extremely important design goal. > I have come up with a way to implement getting a pointer or reference > of the derived class type from a pointer of the base class type What for? I thought the goal was the app not needing to know anything about the library. Thus, the application has no need to get a pointer to the derived class from the base class, ever. If it does, either your interface or the app is broken. > without using a dynamic_cast The library, on the other hand, will need to get its own type from any base type passed in. Now, you say that typically any interface will have just one implementation class in a library. Then the method to get the derived class is simple: use static_cast. There is no way the class could be anything but what you expect. (OK, the application could try to mix implementations, but it would be very stupid.) Static_cast in this case is safe and sound. If there are multiple derived classes, use dynamic_cast. That's what it's for, after all. Better yet, don't cast at all - if there are multiple implementations, chances are that the interface should be rich enough that it suffices even for the library. Or that there is a common base of all library classes that implement the interface that you can use with static_cast. > , and I'm curious about (a) whether this method can > be improved You could make it so that a reader would have even the faintest idea of what's going on. > and (b) whether I'm seriously deluded about doing this (i.e. > am I taking my anti-cast stance too far). Yes, you are. > One "justification" I have > for avoiding dynamic_cast here is that my approach here has a stronger > guarantee than can be provided by dynamic_cast because dynamic_cast has > the possibility of returning a null pointer because the types do not > match up. If you return a pointer in a situation where dynamic_cast returns null, you've a bug, not a stronger guarantee. -- Sebastian Redl [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: blurk on 24 Jan 2008 22:45 Thanks for all the responses so far. I apologise for not checking the actual code compiled before posting (I edited it down from a much bigger test program, and really should have not rushed it so much). Also, as is apparent from some of the responses, the example code is quite unclear. Martin Bonner wrote: > Why does the CLIENT need to know about Derived? Sebastian Redl: >> I have come up with a way to implement getting a pointer or reference >> of the derived class type from a pointer of the base class type > > What for? I thought the goal was the app not needing to know anything about > the library. Thus, the application has no need to get a pointer to the > derived class from the base class, ever. If it does, either your interface > or the app is broken. Of course you are both correct, and my example made it quite unclear that I want the *implementation* to be able to get back the pointer / reference to the implementation type when passed in a pointer of the abstract base type. The whole point of the exercise of hiding the implementation type is because it's none of the client's business. int...(a)gmail.com wrote: > First of all, you don't need dynamic_cast for all downcasts, only for > virtual inheritance, or when you are not sure the cast will succeed. > > On the other hand, you can ensure that cast cannot fail by defining a > private constructor in the interface, and specifying forward-declared > implementation class as a friend class. That way, any valid non-null > pointer to interface is guaranteed to point to an instance of > implementation class (or some of its subclasses). Aah, yes, in my actual interface I am already defining a protected constructor (not private, I know, but at least no public). I just wanted to avoid naming the implementation class (and, more specifically, its namespace(s)) in the interface, and I wanted the implementation class to strictly be a subclass, not a friend, to emphasis the is-a relationship. acehr...(a)gmail.com wrote: > Since the visitor pattern is the answer to the problem in the subject > line, and you don't mention it in your post, I don't know whether you > tried but did not like the results. So, try the visitor pattern. :) And now we get to the point where I don't post a corrected version of my original code because, of course, this is the solution to my problem. I had briefly looked at Alexandrescu's discussion of the visitor pattern (rather than the canonical formulation), and with all the discussion of visiting a hierarchy of polymorphic nodes in some container, I had decided it didn't apply. But it does and looks like it works fairly neatly, without me needed to expose implementation types in the interface. (Well, except for the existence of visitor classes, but they can remain somewhat anonymous.) So thank you for that hint, and making me think harder. It was well worth the effort. Cheers, Bernie -- I do not want email replies, please followup to the group. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
|
Pages: 1 Prev: Unit testing framework for C++ Next: Safe And Efficient Programming With C++ |