I often use this feature to omit detail in interfaces; detail that not
all clients need to know. For example:
class Component {
//...
public:
class Parameter;
virtual Parameter* get_parameter ();
};
Client code can use Component and even pass pointers to Parameters
around while still being blissfully unaware of Parameter's definition.
Clients that need to know can include a separate header where
Parameter is defined.
This works nicely except when the Parameter class really is private
and clients only should see an abstract interface, i.e.:
class Component {
public:
class IParameter {
public:
virtual string get_name () = 0;
virtual string get_value () = 0;
};
virtual IParameter* get_parameter ();
};
Unfortunately, now the implementation of Component and friends, helper
classes and free functions, no longer can use GetParameter without
doing a dynamic_cast should it be necessary to access Parameter
details. In other words, there is type erasure in the Component
interface.
It would be nice if it was possible to *partially* complete a type.
This would mean to specify the base (or bases) of a type without
providing the full definition. For example:
class Component {
public:
class IParameter {
//...
};
class Parameter : public IParameter; // partial type completion
virtual Parameter* get_parameter ();
};
Now the compiler can do a full type-check against what is known about
the Parameter type at any point in the code. The client code knows
only that Parameter inherits IParameter and is hence restricted to use
that interface, while the implementation code that has seen the full
Parameter definition can use all its methods and members; without need
for downcasting.
Any views on the feasability of such a feature?
Regards,
Vidar Hasfjord
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Are you sure you really want to hand out pointers, and raw pointers at that?
> Unfortunately, now the implementation of Component and friends, helper
> classes and free functions, no longer can use GetParameter without
> doing a dynamic_cast should it be necessary to access Parameter
> details. In other words, there is type erasure in the Component
> interface.
>
> It would be nice if it was possible to *partially* complete a type.
> This would mean to specify the base (or bases) of a type without
> providing the full definition. For example:
>
> class Component {
> public:
> class IParameter {
> //...
> };
> class Parameter : public IParameter; // partial type completion
> virtual Parameter* get_parameter ();
> };
>
> Now the compiler can do a full type-check against what is known about
> the Parameter type at any point in the code. The client code knows
> only that Parameter inherits IParameter and is hence restricted to use
> that interface,
So from the client code's point of view a Parameter has the same functionality
as IParameter.
Why not then use IParameter?
> while the implementation code that has seen the full
> Parameter definition can use all its methods and members; without need
> for downcasting.
>
> Any views on the feasability of such a feature?
Cheers, & hth.,
- Alf
There is no reason to declare IParameter's virtual methods as pure -
because the library does in fact provide implementations of those
routines. Pure virtual methods necessitate that the client provide
implementations - but there is no such requirement in this case.
Instead, the Library interface should declare IParameter along these
lines:
class Component
{
public:
...
class IParameter
{
public:
virtual string get_name();
virtual string get_value();
};
virtual IParameter* get_parameter ();
};
The Library then internally defines Component and IParameter
subclasses:
// Internal Library Class Declarations
class ComponentImpl : public Component
{
class Parameter : public IParameter
{
public:
Parameter() : IParameter() {}
virtual ~Parameter() {}
// ...
};
virtual Parameter * get_parameter()
{
return new Parameter;
}
};
Essentially, the Library's factory method returns pointers to concrete
subtypes of the Library's publicly-declared interface classes. In this
way, the Library takes advantage of C++'s dynamic types and virtual
method dispatch to furnish implementations without exposing irrelevant
details.
> It would be nice if it was possible to *partially* complete a type.
> This would mean to specify the base (or bases) of a type without
> providing the full definition.
> ... The client code knows
> only that Parameter inherits IParameter and is hence restricted to use
> that interface, while the implementation code that has seen the full
> Parameter definition can use all its methods and members; without need
> for downcasting.
The solution is for the Library to make only the "Parameter" class
public - and to declare its "IParameter" subclass internally.
> Any views on the feasability of such a feature?
The C++ language already supports this behavior with virtual functions
and dynamic types.
Greg
Cheers
Sfider
Yep, this will work due to covariant return types, which allows you to
strengthen the type info. But it is a lot of scaffolding.
A simpler solution is to just create two interfaces; one for external
use and one for internal use:
class Component {
public:
class IParameter {...};
virtual IParameter* get_parameter ();
private:
class Parameter;
Parameter* private_get_parameter ();
...
};
Implementation:
class Component::Parameter : Component::IParameter {
...
};
IParameter* Component::get_parameter ()
{return private_get_parameter ();}
This works and has less implications on the Component class hierarchy
compared to your solution. But it is still scaffolding. The interface
of Component is duplicated and one implemented in terms of the other.
It would be much more straight-forward to be able to tell the compiler
partial type information. Here's that hypothetical solution again:
class Component {
public:
class IParameter {...};
class Parameter : public IParameter; // partial type completion
virtual Parameter* get_parameter ();
private:
...
};
But is it possible/easy to implement? Does the compiler know enough
after seeing the partial type to use Parameter through the base class
IParameter?
Regards,
Vidar Hasfjord
The code examples are just for exposition. I avoided smart pointers in
the examples to keep it simple; also there are some dangerous
implications of using smart pointers with incomplete types (ref.
deleting an incomplete type). Since the post was about incomplete
types I tried to stay on safe ground.
> Why not then use IParameter?
See Greg's post and my follow-up.
Regards,
Vidar Hasfjord