Connecting Tech Pros Worldwide Forums | Help | Site Map

Restricting base class visibility

Rennie deGraaf
Guest
 
Posts: n/a
#1: Jan 5 '06
I have a system that looks like this:

class AbstractBase { /* ... */ };
template <class T> class Impl : public AbstractBase { /* ... */ };
// ...
Impl<int> i;
Impl<float> f;
std::vector<AbstractBase*> vec;
vec.push_back(&i);
vec.push_back(&f);
// ...

It works. However, at the moment, class AbstractBase has public
visibility - anyone can subclass it. Ideally, the only subclasses of
AbstractBase should be instantiations of Impl. Impl, on the other hand,
can (and should) be instantiable by anyone. Is it possible for me to
somehow hide AbstractBase to restrict what classes can subclass it?

Thanks,
Rennie deGraaf

Shezan Baig
Guest
 
Posts: n/a
#2: Jan 5 '06

re: Restricting base class visibility


Rennie deGraaf wrote:[color=blue]
> I have a system that looks like this:
>
> class AbstractBase { /* ... */ };
> template <class T> class Impl : public AbstractBase { /* ... */ };
> // ...
> Impl<int> i;
> Impl<float> f;
> std::vector<AbstractBase*> vec;
> vec.push_back(&i);
> vec.push_back(&f);
> // ...
>
> It works. However, at the moment, class AbstractBase has public
> visibility - anyone can subclass it. Ideally, the only subclasses of
> AbstractBase should be instantiations of Impl. Impl, on the other hand,
> can (and should) be instantiable by anyone. Is it possible for me to
> somehow hide AbstractBase to restrict what classes can subclass it?[/color]


You can make the constructor of AbstractBase private, then make Impl a
friend of AbstractBase. That way, only Impl knows how to construct the
AbstractBase sub-object.

But I'm curious, why restrict the use of AbstractBase in this way?

Hope this helps,
-shez-

Greg
Guest
 
Posts: n/a
#3: Jan 5 '06

re: Restricting base class visibility



Rennie deGraaf wrote:[color=blue]
> I have a system that looks like this:
>
> class AbstractBase { /* ... */ };
> template <class T> class Impl : public AbstractBase { /* ... */ };
> // ...
> Impl<int> i;
> Impl<float> f;
> std::vector<AbstractBase*> vec;
> vec.push_back(&i);
> vec.push_back(&f);
> // ...
>
> It works. However, at the moment, class AbstractBase has public
> visibility - anyone can subclass it. Ideally, the only subclasses of
> AbstractBase should be instantiations of Impl. Impl, on the other hand,
> can (and should) be instantiable by anyone. Is it possible for me to
> somehow hide AbstractBase to restrict what classes can subclass it?[/color]

The obvious question to ask is why are AbstractBase and Impl distinct
classes to start with, if AbstractBase is meant to have only a single
subclass.

Greg

Shezan Baig
Guest
 
Posts: n/a
#4: Jan 5 '06

re: Restricting base class visibility


Greg wrote:[color=blue]
> The obvious question to ask is why are AbstractBase and Impl distinct
> classes to start with, if AbstractBase is meant to have only a single
> subclass.[/color]

[color=blue]
>From the OP's code, it looks like he wants to create a vector[/color]
containing heterogenous objects. The problem with his approach is that
the <AbstractBase*> parameter for vector does not preserve value
semantics. A better approach would be to use something boost::any or
boost::variant, where value-semantics are maintained.

-shez-

Rennie deGraaf
Guest
 
Posts: n/a
#5: Jan 5 '06

re: Restricting base class visibility


Greg wrote:[color=blue]
> Rennie deGraaf wrote:
>[color=green]
>>I have a system that looks like this:
>>
>> class AbstractBase { /* ... */ };
>> template <class T> class Impl : public AbstractBase { /* ... */ };
>> // ...
>> Impl<int> i;
>> Impl<float> f;
>> std::vector<AbstractBase*> vec;
>> vec.push_back(&i);
>> vec.push_back(&f);
>> // ...
>>
>>It works. However, at the moment, class AbstractBase has public
>>visibility - anyone can subclass it. Ideally, the only subclasses of
>>AbstractBase should be instantiations of Impl. Impl, on the other hand,
>>can (and should) be instantiable by anyone. Is it possible for me to
>>somehow hide AbstractBase to restrict what classes can subclass it?[/color]
>
>
> The obvious question to ask is why are AbstractBase and Impl distinct
> classes to start with, if AbstractBase is meant to have only a single
> subclass.
>
> Greg[/color]

What I need is an STL container (I'm actually using an std::map, but an
std::vector was simpler for the example) containing a bunch of different
instantiations of Impl. In other words, a container can can store an
Impl<int> and an Impl<string>, but not an int or a string. AFAIK, C++
doesn't define references or pointers to templates, only to template
instantiations, so I need to refer to objects of Impl<int> and
Impl<string> through a base class.

Since the STL doesn't seem to play nicely with polymorphic types, I have
to store pointers to my objects, rather than the objects themselves, and
handle allocation myself. It's a pain, but I don't know any better
ways. I'm open to suggestions, if you have them.

Rennie
Neelesh Bodas
Guest
 
Posts: n/a
#6: Jan 5 '06

re: Restricting base class visibility


Rennie deGraaf wrote:[color=blue]
>
> What I need is an STL container (I'm actually using an std::map, but an
> std::vector was simpler for the example) containing a bunch of different
> instantiations of Impl. In other words, a container can can store an
> Impl<int> and an Impl<string>, but not an int or a string. AFAIK, C++
> doesn't define references or pointers to templates, only to template
> instantiations, so I need to refer to objects of Impl<int> and
> Impl<string> through a base class.
>
> Since the STL doesn't seem to play nicely with polymorphic types, I have
> to store pointers to my objects, rather than the objects themselves, and
> handle allocation myself. It's a pain, but I don't know any better
> ways. I'm open to suggestions, if you have them.[/color]

As shezan suggested, you can solve this by having private Constructor
in AbstractBase

#include <vector>

class AbstractBase {
protected:
virtual ~AbstractBase() { }
private:
AbstractBase(){ }
template<class T> friend class Impl;

};

template <class T> class Impl : public AbstractBase {
T i;
};


class Zoo : public AbstractBase {
};
// Zoo cannot be instantiated because constructor of base class is
private.

int main()
{
Impl<int> i;
Impl<float> f;
// Zoo z; // not allowed

std::vector<AbstractBase*> v;
v.push_back(&i);
v.push_back(&f);

// v.push_back(&z); // Error.

}

Shezan Baig
Guest
 
Posts: n/a
#7: Jan 5 '06

re: Restricting base class visibility


Rennie deGraaf wrote:[color=blue]
> Since the STL doesn't seem to play nicely with polymorphic types, I have
> to store pointers to my objects, rather than the objects themselves, and
> handle allocation myself. It's a pain, but I don't know any better
> ways. I'm open to suggestions, if you have them.[/color]


I guess I should rephrase my original question so: What virtual
functions are you going to have in 'AbstractBase'? If there are no
virtual functions, there should be no need for inheritance. If there
*are* virtual functions, when why make 'AbstractBase' completely
hidden?

I suggested 'boost::any' earlier, but if you want to restrict your
container to only contain 'Impl' objects, then it is reasonably
straightforward to adapt 'boost::any' to restrict only 'Impl' types.
Something like:


class AnyImpl {
// PRIVATE DATA MEMBERS
boost::any d_any;

public:
template <typename TYPE>
AnyImpl(const Impl<TYPE>& value) : d_any(value) { }

template <typename TYPE>
AnyImpl& operator=(const Impl<TYPE>& value) { d_any = value; }
};


Now you can create vector<AnyImpl> or map<AnyImpl, AnyImpl>.


Hope this helps,
-shez-

Rennie deGraaf
Guest
 
Posts: n/a
#8: Jan 5 '06

re: Restricting base class visibility


Shezan Baig wrote:[color=blue]
> Rennie deGraaf wrote:
>[color=green]
>>I have a system that looks like this:
>>
>> class AbstractBase { /* ... */ };
>> template <class T> class Impl : public AbstractBase { /* ... */ };
>> // ...
>> Impl<int> i;
>> Impl<float> f;
>> std::vector<AbstractBase*> vec;
>> vec.push_back(&i);
>> vec.push_back(&f);
>> // ...
>>
>>It works. However, at the moment, class AbstractBase has public
>>visibility - anyone can subclass it. Ideally, the only subclasses of
>>AbstractBase should be instantiations of Impl. Impl, on the other hand,
>>can (and should) be instantiable by anyone. Is it possible for me to
>>somehow hide AbstractBase to restrict what classes can subclass it?[/color]
>
>
> You can make the constructor of AbstractBase private, then make Impl a
> friend of AbstractBase. That way, only Impl knows how to construct the
> AbstractBase sub-object.[/color]

Of course, doing this would make *all* of AbstractBase's private methods
and data accessible to Impl. Is it possible to only make Impl's
constructor a friend to AbstractBase? I can't find anything on the
subject, and can't figure out any syntax that compiles.
[color=blue]
> But I'm curious, why restrict the use of AbstractBase in this way?[/color]

My code assumes that all objects that it pulls out of vec are
instantiations of Impl. If someome subclasses AbstractBase with
something else and gets it into vec, then my code with throw an
exception with an error message that assumes a completely different
problem. My code can't tell the difference between a specialization of
Impl that it doesn't know about and something that isn't an Impl at all.

Also, I intend people using my system to write specializations of Impl,
and don't want to make it possible for people to get confused and try to
subclass AbstractBase instead. If I make Impl a friend of AbstractBase
and make AbstractBase's constructor private, then this can't happen, but
then people might mess around with AbstractBase's private data and
totally s*crew up the system. Neither is a critical problem (unless
someome finds a way to make a buffer overflow or something out of it),
but I'd rather have neither, and I'm not sure which is worse.

Rennie
Greg Herlihy
Guest
 
Posts: n/a
#9: Jan 5 '06

re: Restricting base class visibility


> Rennie deGraaf" <ca.ucalgary.cpsc@degraaf> wrote:[color=blue][color=green]
>> Rennie deGraaf wrote:
>>[color=darkred]
>>> I have a system that looks like this:
>>>
>>> class AbstractBase { /* ... */ };
>>> template <class T> class Impl : public AbstractBase { /* ... */ };
>>> // ...
>>> Impl<int> i;
>>> Impl<float> f;
>>> std::vector<AbstractBase*> vec;
>>> vec.push_back(&i);
>>> vec.push_back(&f);
>>> // ...
>>>
>>> It works. However, at the moment, class AbstractBase has public
>>> visibility - anyone can subclass it. Ideally, the only subclasses of
>>> AbstractBase should be instantiations of Impl. Impl, on the other hand,
>>> can (and should) be instantiable by anyone. Is it possible for me to
>>> somehow hide AbstractBase to restrict what classes can subclass it?[/color][/color]
>
> What I need is an STL container (I'm actually using an std::map, but an
> std::vector was simpler for the example) containing a bunch of different
> instantiations of Impl. In other words, a container can can store an
> Impl<int> and an Impl<string>, but not an int or a string. AFAIK, C++
> doesn't define references or pointers to templates, only to template
> instantiations, so I need to refer to objects of Impl<int> and
> Impl<string> through a base class.
>
> Since the STL doesn't seem to play nicely with polymorphic types, I have
> to store pointers to my objects, rather than the objects themselves, and
> handle allocation myself. It's a pain, but I don't know any better
> ways. I'm open to suggestions, if you have them.[/color]

I would suggest looking into a discriminated union or similar, variant type
(such as Boost's variant) as an alternative solution. I think either of
those approaches would involve less "housekeeping" chores, require fewer
lines code, and overall be simply easier to manage than the implementation
described above. The benefit of a union or variant class derives from having
just one class do the work currently slated for one base class and several,
derived classes - in short - an entire class hierarchy.

Greg

Bo Persson
Guest
 
Posts: n/a
#10: Jan 10 '06

re: Restricting base class visibility



"Neelesh Bodas" <neelesh.bodas@gmail.com> skrev i meddelandet
news:1136435196.953844.8760@g14g2000cwa.googlegrou ps.com...[color=blue]
> Rennie deGraaf wrote:[color=green]
>>
>> What I need is an STL container (I'm actually using an std::map,
>> but an
>> std::vector was simpler for the example) containing a bunch of
>> different
>> instantiations of Impl. In other words, a container can can store
>> an
>> Impl<int> and an Impl<string>, but not an int or a string. AFAIK,
>> C++
>> doesn't define references or pointers to templates, only to
>> template
>> instantiations, so I need to refer to objects of Impl<int> and
>> Impl<string> through a base class.
>>
>> Since the STL doesn't seem to play nicely with polymorphic types, I
>> have
>> to store pointers to my objects, rather than the objects
>> themselves, and
>> handle allocation myself. It's a pain, but I don't know any better
>> ways. I'm open to suggestions, if you have them.[/color]
>
> As shezan suggested, you can solve this by having private
> Constructor
> in AbstractBase
>
> #include <vector>
>
> class AbstractBase {
> protected:
> virtual ~AbstractBase() { }
> private:
> AbstractBase(){ }
> template<class T> friend class Impl;
>
> };
>
> template <class T> class Impl : public AbstractBase {
> T i;
> };
>
>
> class Zoo : public AbstractBase {
> };
> // Zoo cannot be instantiated because constructor of base class is
> private.[/color]

But it doesn't stop the pervasive intruder!

struct ooZ {};

template<>
class Impl<ooZ> : public AbstractBase {
// whatever Zoo wants to do!
};

typedef Impl<ooZ> Zoo;

[color=blue]
>
> int main()
> {
> Impl<int> i;
> Impl<float> f;
> // Zoo z; // not allowed[/color]

Now it is allowed!
[color=blue]
>
> std::vector<AbstractBase*> v;
> v.push_back(&i);
> v.push_back(&f);
>
> // v.push_back(&z); // Error.
>
> }
>[/color]


mlimber
Guest
 
Posts: n/a
#11: Jan 10 '06

re: Restricting base class visibility


Rennie deGraaf wrote:[color=blue]
> I have a system that looks like this:
>
> class AbstractBase { /* ... */ };
> template <class T> class Impl : public AbstractBase { /* ... */ };
> // ...
> Impl<int> i;
> Impl<float> f;
> std::vector<AbstractBase*> vec;
> vec.push_back(&i);
> vec.push_back(&f);
> // ...
>
> It works. However, at the moment, class AbstractBase has public
> visibility - anyone can subclass it. Ideally, the only subclasses of
> AbstractBase should be instantiations of Impl. Impl, on the other hand,
> can (and should) be instantiable by anyone. Is it possible for me to
> somehow hide AbstractBase to restrict what classes can subclass it?
>
> Thanks,
> Rennie deGraaf[/color]

In addition to the comments you've already received and depending on
what direction your design takes, you might be interested in this
article on the Attorney-Client idiom:

http://www.cuj.com/documents/s=8188/...601bolton.html

Cheers! --M

Earl Purple
Guest
 
Posts: n/a
#12: Jan 11 '06

re: Restricting base class visibility



Rennie deGraaf wrote:
[color=blue]
> Shezan Baig wrote:[color=green]
> > You can make the constructor of AbstractBase private, then make Impl a
> > friend of AbstractBase. That way, only Impl knows how to construct the
> > AbstractBase sub-object.[/color]
>
> Of course, doing this would make *all* of AbstractBase's private methods
> and data accessible to Impl. Is it possible to only make Impl's
> constructor a friend to AbstractBase? I can't find anything on the
> subject, and can't figure out any syntax that compiles.[/color]

That would not be the safest thing to do as anyone can specialise their
Impl template.

There is a way to be restrictive this way.

Have another class that implements all the private parts of
AbstractBase, make AbstractBase a friend of that class and then make
Impl a friend of AbstractBase.Then Impl will only have access to the
constructor of AbstractBase but not the remainder as friendship isn't
transitive.

I don't know your full model but if it involves casting down the
hierarchy later on then it is probably wrong. The only times I use
casting is when I am loading objects from a library and have to check
that the type I have loaded is the type I expected to receive. It also
happens to fit in well with a generic loading model that can store in
one collection lots of different factory types.

mlimber
Guest
 
Posts: n/a
#13: Jan 11 '06

re: Restricting base class visibility


Earl Purple wrote:[color=blue]
> Rennie deGraaf wrote:[color=green]
> > Shezan Baig wrote:[color=darkred]
> > > You can make the constructor of AbstractBase private, then make Impl a
> > > friend of AbstractBase. That way, only Impl knows how to construct the
> > > AbstractBase sub-object.[/color]
> >
> > Of course, doing this would make *all* of AbstractBase's private methods
> > and data accessible to Impl. Is it possible to only make Impl's
> > constructor a friend to AbstractBase? I can't find anything on the
> > subject, and can't figure out any syntax that compiles.[/color]
>
> That would not be the safest thing to do as anyone can specialise their
> Impl template.
>
> There is a way to be restrictive this way.
>
> Have another class that implements all the private parts of
> AbstractBase, make AbstractBase a friend of that class and then make
> Impl a friend of AbstractBase.Then Impl will only have access to the
> constructor of AbstractBase but not the remainder as friendship isn't
> transitive.[/color]

[snip]

This is the Attorney-Client idiom described in the article I cited in
my first post in this thread.

Cheers! --M

Closed Thread