467,101 Members | 1,144 Online
Bytes | Developer Community
Ask Question

Home New Posts Topics Members FAQ

Post your question to a community of 467,101 developers. It's quick & easy.

Circular dependencies in Templates - better solution?

Hello everyone!

I am a newbie to C++ (~1 Week experience) and I have a few months of
experience with object-oriented languages (Objective-C). I am
currently working just for fun on a particle system.

All the particles are controlled by a "server". The server performs
all kinds of operations on them (updating, drawing etc.). The
particles (my "clients") on the other hand need to retrieve once a
while some information from their server - they need to be hooked up
to it. This works perfectly alright as long as I only have one
server-class and one client-class. However if I want to add some
subclasses and make everything more customizable I run into severe
problems. The lack of run-time dynamism caused some problems on
creating the server/client class structure.
If I use a new client-class it will most likely need a new
server-class. However its superclass already inherits a connection to
the old server-class. I would need to re-implement all the member
functions for the new server-class. In a pure dynamic language
(Smalltalk/Objective-C) I could simply ignore the static type
checking. To avoid compiler warnings I would simply cast the
object-pointers when necessary (just for cosmetic reasons).
C++ would not be C++ if it wouldn't deliver a solution for this
dilemma - Templates. When I need a new client class, I just need to
pass a new type parameter. Nice.
And C++ wouldn't be C++ if no new problems would arise. I will
demonstrate it.
My server-class implementations:
template <class GenericClient>
class ServerClass
{
public:
ServerClass()
{
clients = vector<GenericClient> (10, GenericClient(*this));
}
protected:
vector<GenericClient> clients;
int foo;
};

template <class GenericClient>
class ServerSubclass : public ServerClass<GenericClient>
{
public:
ServerSubclass() {}
protected:
int bar;
};
No problem until now. But now the client-class implementations:
template <class GenericServer>
class ClientClass
{
public:
ClientClass() : server(NULL) {}
ClientClass(GenericServer *aServer) { server = aServer; }
protected:
GenericServer *server;
};

template <class GenericServer>
class ClientSubclass : public ClientClass<GenericServer>
{
public:
ClientSubclass() {}
ClientSubclass(GenericServer *aServer) :
ClientClass<GenericServer>(aServer){}
protected:
int bla;
};
You can see that every client gets a connection to its server, when it
is constructed. But now try to instantiate a server with a
client-class.
ServerSubclass<ClientSubclass<ServerSubclass<Clien tSubclass...> > >
We get an infinite circular dependency between the template classes.
It is impossible to solve this circular dependency unless I have
missed something fundamental about C++ templates.
However there is a not so elegant way to do it with partially
resorting to traditional OO constructions. All I need to do is to
rewrite my client-subclass without templates.
class ClientSubclass : public
ClientClass<ServerSubclass<ClientSubclass> >
{
public:
ClientSubclass() {}
ClientSubclass(ServerSubclass<ClientSubclass> *aServer) :
ClientClass<ServerSubclass<ClientSubclass> >(aServer) {}
protected:
int bla;
};
This looks confusing and it is indeed. But it is now possible to
instantiate a server.
ServerSubclass<ClientSubclass> myServer();
However subclassing without a template is not only a way to solve the
circular-dependency-problem but also to prevent any further
subclassing. If I wanted to subclass my first-subclass with all its
member functions I would need to convert my first subclass to a
template class and then subclass it twice (once for the first and once
for the second non-template subclass).

Am I totally wrong? Have I totally missed something about OO design?
Is there a more convenient way of doing this in C++ ?

Any comments appreciated ;-)

TIA
Robert Potthast
Jul 23 '05 #1
  • viewed: 4095
Share:
4 Replies
ro86 wrote in news:dd**************************@posting.google.c om in
comp.lang.c++:

ServerSubclass<ClientSubclass<ServerSubclass<Clien tSubclass...> > >
We get an infinite circular dependency between the template classes.
It is impossible to solve this circular dependency unless I have
missed something fundamental about C++ templates.
However there is a not so elegant way to do it with partially
resorting to traditional OO constructions. All I need to do is to
rewrite my client-subclass without templates.


What you need is template template paramiters:

#include <iostream>
#include <ostream>
#include <vector>
template < template <typename> class GenericClient >
class ServerClass
{
public:
ServerClass() :
clients(
std::vector< GenericClient< ServerClass > > (
10, GenericClient< ServerClass >(this)
)
)
{
}

void print()
{
std::size_t i, len = clients.size();
for ( i =0; i < len; ++i )
{
clients[i].print();
}
}

protected:
std::vector< GenericClient<ServerClass> > clients;
int foo;
};

template < template <typename> class GenericClient >
class ServerSubclass : public ServerClass<GenericClient>
{
public:
ServerSubclass() {}
protected:
int bar;
};
template <typename GenericServer>
class ClientClass
{
public:
ClientClass() : server(NULL) {}
ClientClass(GenericServer *aServer) { server = aServer; }
virtual void print() {}
protected:
GenericServer *server;
};
template <typename GenericServer>
class ClientSubclass : public ClientClass< GenericServer >
{
public:
ClientSubclass() {}
ClientSubclass(GenericServer *aServer) :
ClientClass< GenericServer >(aServer), bla( 0 )
{
}
ClientSubclass( ClientSubclass const &rhs ) : bla( ++rhs.bla )
{
}
virtual void print() { std::cout << bla << '\n'; }
protected:
mutable int bla;
};

ServerSubclass< ClientSubclass > server;

int main()
{
server.print();
}

HTH.

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 23 '05 #2
Rob Williscroft wrote:
template < template <typename> class GenericClient >
class ServerClass
{
//...
}
Ok, this one is not easy.
The server-class template assumes that you pass a class which is again
a template as parameter. Right?
std::vector< GenericClient< ServerClass > > (
10, GenericClient< ServerClass >(this)
I think this is the tricky part. The compiler knows that the
GenericClient awaits another class as parameter. If we would write this
outside of our template we would have a circular (or recursive)
dependency. But now we can pass the template class for our client as
parameter and thus avoid the circular dependency.
HTH.

Yes, it did ;)
Thanks!

Jul 23 '05 #3
wrote in news:11*********************@g14g2000cwa.googlegro ups.com in
comp.lang.c++:
Rob Williscroft wrote:
template < template <typename> class GenericClient >
class ServerClass
{
//...
}
Ok, this one is not easy.
The server-class template assumes that you pass a class which is again
a template as parameter. Right?


The paramiter is template-template paramiter and it must
be passed a class-template that takes one type paramiter.
std::vector< GenericClient< ServerClass > > (
10, GenericClient< ServerClass >(this)


I think this is the tricky part. The compiler knows that the
GenericClient awaits another class as parameter. If we would write this
outside of our template we would have a circular (or recursive)
dependency. But now we can pass the template class for our client as
parameter and thus avoid the circular dependency.


Well the bit you quoted is inside a constructor, templates and there
members are only instantiated when they're needed. Before it
instantiates the constructor it will instantiate the class to do that
it needs to instantiate the data member:

std::vector< GenericClient<ServerClass> > clients;

to do that it may need to instantiate:

GenericClient<ServerClass>

Fortunatly this can be done without knowing anything about
ServerClass as neither:

ClientSubclass< ServerClass > or
ClientSubclass< ServerSubClass >

require knowing anything about ServerClass or ServerSubClass
as they only use a pointers to a ServerClass.

The terminoligy used to refere to this is "ClientSubclass can
be instantiated with an incomplete type", ServerSubClass (and
ServerClass) being the incomplete (because they're currently
being instatiated) types.

Also I noticed that the code I posted may not do precisley
what you wanted, as the ServerSubClass contains a std::vector
of ClientSubClass< ServerClass > where as IIUC you wanted
a vector of ClientSubClass< ServerSubClass >.

Here's the fixed code, it has an extra paramiter to ServerClass
to feed in the sub-class type and a static_cast to cast from the
base type to the derived type:

#include <iostream>
#include <ostream>
#include <vector>
template < template <typename> class GenericClient, typename Server >
class ServerClass
{
public:
ServerClass() :
clients(
std::vector< GenericClient< Server > > (
10, GenericClient< Server >(static_cast< Server *>( this ) )
)
)
{
}

void print()
{
std::size_t i, len = clients.size();
for ( i =0; i < len; ++i )
{
clients[i].print();
}
}

protected:
std::vector< GenericClient< Server > > clients;
int foo;
};
template < template <typename> class GenericClient >
class ServerSubclass :
public ServerClass<GenericClient, ServerSubclass< GenericClient > >
{
public:
ServerSubclass() {}
protected:
int bar;
};
template <typename GenericServer>
class ClientClass
{
public:
ClientClass() : server(NULL) {}
ClientClass(GenericServer *aServer) { server = aServer; }
virtual void print() {}
protected:
GenericServer *server;
};
template <typename GenericServer>
class ClientSubclass : public ClientClass< GenericServer >
{
public:
ClientSubclass() {}
ClientSubclass(GenericServer *aServer) :
ClientClass< GenericServer >(aServer), bla( 0 )
{
}
ClientSubclass( ClientSubclass const &rhs ) : bla( ++rhs.bla )
{
}
virtual void print() { std::cout << bla << '\n'; }
protected:
mutable int bla;
};

ServerSubclass< ClientSubclass > server;
int main()
{
server.print();
}

Rob.
--
http://www.victim-prime.dsl.pipex.com/
Jul 23 '05 #4
This looks like the final solution. The original implementation hooked
the ClientSubclass to the ServerClass up. All I now need to do is to
wrap my classes into a elegant typedef and I can go on.
This has been very helpful. Problems like these are the only way to
learn more about complex languages like C++. The concept of templates
is fascinating because its very flexible but offers very high
performance (A glimpse into the future of meta-programming?). Learning
by doing =)
Danke!

Jul 23 '05 #5

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

12 posts views Thread by jinal jhaveri | last post: by
1 post views Thread by Henry Miller | last post: by
2 posts views Thread by ernesto basc?n pantoja | last post: by
16 posts views Thread by PromisedOyster | last post: by
3 posts views Thread by Keith F. | last post: by
7 posts views Thread by barias@axiscode.com | last post: by
8 posts views Thread by nyhetsgrupper@gmail.com | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.