473,395 Members | 2,010 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,395 software developers and data experts.

Cyclic includes / forward definitions always solvable?

Hi all,
Regarding those cyclic dependencies of classes (like cases in which
class A refers to class B and class B to class A): can they ALL be
resolved by forward defining classes, and by implementing the actual
functions outside the class definition, OR there are STILL cases which
cannot be solved in this way and could require rewriting your code /
rewriting your class hierarchy?
Jul 22 '05 #1
9 1589
On Mon, 01 Mar 2004 13:14:30 -0600, John Doe <jo*****@nowhere.com> wrote:
Hi all,
Regarding those cyclic dependencies of classes (like cases in which
class A refers to class B and class B to class A): can they ALL be
resolved by forward defining classes, and by implementing the actual
functions outside the class definition, OR there are STILL cases which
cannot be solved in this way and could require rewriting your code /
rewriting your class hierarchy?


Resolving cyclic dependencies has nothing to do with where you put the
actual function implementations (including in the case of inline functions,
since they can be defined "ex-situ"); it does have to do with whether or
not the compiler has seen a "complete definition" of the class at the point
where that information is required. Typically, the information of interest
is the storage requirement for instances of that class.

Note that a class definition would be considered complete even if it
includes /pointers to incomplete types/.

As far as I know, it all boils down to this: you cannot write any
declaration or statement that requires the compiler to know how big a class
is before you've provided a complete definition of that class.
-leor

Leor Zolman
BD Software
le**@bdsoft.com
www.bdsoft.com -- On-Site Training in C/C++, Java, Perl & Unix
C++ users: Download BD Software's free STL Error Message
Decryptor at www.bdsoft.com/tools/stlfilt.html
Jul 22 '05 #2
"John Doe" <jo*****@nowhere.com> wrote in message
news:c2**********@news.vanderbilt.edu...
Regarding those cyclic dependencies of classes (like cases in which
class A refers to class B and class B to class A): can they ALL be
resolved by forward defining classes, and by implementing the actual
functions outside the class definition, OR there are STILL cases which
cannot be solved in this way and could require rewriting your code /
rewriting your class hierarchy?


A workaround is always possible, but there are still some
tough cases that require an alteration of the design.
For example, how would you use the following:

struct A {
std::list<B> myBs;
};

struct B {
std::list<A> myAs;
};

For the next C++ standard, I believe that it is being
considered to explicitly allow the the instantiation
of some standard containers (e.g. std::vector)
with incomplete types.
But there is no such guarantee currently.

Another classic problem is: how do you define a function f(void)
that returns a pointer to an f(void) ?

A workaround is to use an intermediate structure:
struct FP {
FP (*fp)(void);
};

FP f(void)
{
FP ans = {f};
return ans;
}

This idiom is actually useful in some implementations
of a state machine.

Regards,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- e-mail contact form
Jul 22 '05 #3
Ivan Vecerina wrote:
"John Doe" <jo*****@nowhere.com> wrote in message
news:c2**********@news.vanderbilt.edu...
Regarding those cyclic dependencies of classes (like cases in which
class A refers to class B and class B to class A): can they ALL be
resolved by forward defining classes, and by implementing the actual
functions outside the class definition, OR there are STILL cases which
cannot be solved in this way and could require rewriting your code /
rewriting your class hierarchy?

A workaround is always possible, but there are still some
tough cases that require an alteration of the design.
For example, how would you use the following:

struct A {
std::list<B> myBs;
};

struct B {
std::list<A> myAs;
};


I don't particularly understand the usefulness of this example.

A is a strcuture that contains a list of B's, which in turns contains a
list of A's, which contains a list of B's, which conains a list of
A's....., you get the picture.

I think it IS a good thing that compilers don't allow such horrible
things...
Jul 22 '05 #4
Jorge Rivera wrote:

For example, how would you use the following:

struct A {
std::list<B> myBs;
};

struct B {
std::list<A> myAs;
};

I don't particularly understand the usefulness of this example.

A is a strcuture that contains a list of B's, which in turns contains a
list of A's, which contains a list of B's, which conains a list of
A's....., you get the picture.


I don't. In this case the list can be empty, which means that this
recursion is not necessarily infinite.

What you are saying is essentially the same as saying that functional
recursion is impossible because it never ends.
I think it IS a good thing that compilers don't allow such horrible
things...


I don't see anything horrible with it. It think you are mistaking this
situation with other situation, which employs direct aggregation

struct A { B b; };
struct B { A a; };
// Impossible and makes no sense

This is not the same as the previous example with 'std::list's, since
'std::list's implement indirect aggregation.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #5
The following article describes techniques for resolving such includes:

http://www.eventhelix.com/RealtimeMa...dePatterns.htm

Sandeep
--
http://www.EventHelix.com/EventStudio
EventStudio 2.0 - System Architecture Design CASE Tool
Jul 22 '05 #6
> A workaround is always possible, but there are still some
tough cases that require an alteration of the design.
For example, how would you use the following:

struct A {
std::list<B> myBs;
};

struct B {
std::list<A> myAs;
};
This compiles just fine in VC++7.1 ...

For the next C++ standard, I believe that it is being
considered to explicitly allow the the instantiation
of some standard containers (e.g. std::vector)
with incomplete types.
But there is no such guarantee currently.
You mean that they are doing particular cases in the compiler like Java
is doing for Strings? That would be really bad. I hate that: a language
should be just a language and should treat all data types equally.

But anyway I don't think it's the reason for which it compiles. I tried
with my own template instead of std::string and it compiled on the same,
because the std::string basically just uses pointers to the classes, so
it just requires a forward definition of such classes in order to
compile correctly, right?

Another classic problem is: how do you define a function f(void)
that returns a pointer to an f(void) ?

A workaround is to use an intermediate structure:
struct FP {
FP (*fp)(void);
};

FP f(void)
{
FP ans = {f};
return ans;
}


This was GREAT code, unfortunately it does not compile.

I guess it is because in the implementation of f you are putting a
pointer to a global function, while the definition of FP requires a
__thiscall function pointer (a function member of a class, in which
"this" is passed secretely). I'm NOT sure though.

Is there any cast which can make the above code compile? I tried but I
wasn't able to do it.

Thanks
Jul 22 '05 #7
"John Doe" <jo*****@nowhere.com> wrote in message
news:c2**********@news.vanderbilt.edu...
A workaround is always possible, but there are still some
tough cases that require an alteration of the design.
For example, how would you use the following:

struct A {
std::list<B> myBs;
};

struct B {
std::list<A> myAs;
};
This compiles just fine in VC++7.1 ...


But is not currently *guaranteed* to work ( => not legal C++ ).
Container implementations would be allowed, for example,
to store a root element within the class itself,
or may otherwise use the sizeof() of the provided
parameter to select a data member or specialization,
or whatever...

So the code is not (formally) portable, even though
it might work with all your compilers in practice.
For the next C++ standard, I believe that it is being
considered to explicitly allow the the instantiation
of some standard containers (e.g. std::vector)
with incomplete types.
But there is no such guarantee currently.


You mean that they are doing particular cases in the compiler like Java
is doing for Strings? That would be really bad. I hate that: a language
should be just a language and should treat all data types equally.


I did not mean that this would only apply to std::vector.
The fact is: the code above works well with several implementations.
But the standard allows standard library implementations to break it
(e.g. it could be needed to allow some optimizations on some platforms).
So declaring the above code above legal requires a cost/benefit analysis,
and maybe it cannot apply to all containers (e.g. std::string,
which often stores a small buffer as a data member, would not work).
But anyway I don't think it's the reason for which it compiles. I tried
with my own template instead of std::string and it compiled on the same,
because the std::string basically just uses pointers to the classes, so
it just requires a forward definition of such classes in order to
compile correctly, right? Right - in your implementation.
Another classic problem is: how do you define a function f(void)
that returns a pointer to an f(void) ?

A workaround is to use an intermediate structure:
struct FP {
FP (*fp)(void);
};

FP f(void)
{
FP ans = {f};
return ans;
}


This was GREAT code, unfortunately it does not compile.

It compiles as is on Comeau++, which is considered by many as being
a reference in compliance (http://www.comeaucomputing.com/tryitout/).

What is the exact error message you are getting?
I guess it is because in the implementation of f you are putting a
pointer to a global function, while the definition of FP requires a
__thiscall function pointer (a function member of a class, in which
"this" is passed secretely). I'm NOT sure though.
The sample above does not involve any member functions, just
a data member that is of type pointer-to-function.
Is there any cast which can make the above code compile?
I tried but I wasn't able to do it.


Please post the exact code you tried and associated error message.
The snippet is 'kosher' as far as I can tell (although it is not
an example of clarity...).
Cheers,
Ivan
--
http://ivan.vecerina.com/contact/?subject=NG_POST <- e-mail contact form
Jul 22 '05 #8
Ivan Vecerina wrote:
"John Doe" <jo*****@nowhere.com> wrote in message
news:c2**********@news.vanderbilt.edu...
A workaround is always possible, but there are still some
tough cases that require an alteration of the design.
For example, how would you use the following:

struct A {
std::list<B> myBs;
};

struct B {
std::list<A> myAs;
};


This compiles just fine in VC++7.1 ...

But is not currently *guaranteed* to work ( => not legal C++ ).
Container implementations would be allowed, for example,
to store a root element within the class itself,
or may otherwise use the sizeof() of the provided
parameter to select a data member or specialization,
or whatever...


??
In that case it's VERY RIGHT that it cannot compile, because it would
allocate an infinite-size structure due to recursion. Of course this is
off the topic of my initial question: I of course never intended to
compile impossible data structures.
A workaround is to use an intermediate structure:
struct FP {
FP (*fp)(void);
};

FP f(void)
{
FP ans = {f};
return ans;
}
I guess it is because in the implementation of f you are putting a
pointer to a global function, while the definition of FP requires a
__thiscall function pointer (a function member of a class, in which
"this" is passed secretely). I'm NOT sure though.

The sample above does not involve any member functions, just
a data member that is of type pointer-to-function.


Really?
If you do

FP fp;
//....
*(fp.f)();

you think that the f function called would not get a this pointer?
I think it would!

Is there any cast which can make the above code compile?
I tried but I wasn't able to do it.

Please post the exact code you tried and associated error message.
The snippet is 'kosher' as far as I can tell (although it is not
an example of clarity...).

Exactly your code.
The error I get is:

error C2440: 'initializing' : cannot convert from 'FP (__cdecl *)(void)'
to 'FP (__cdecl *)(void)'
Incompatible calling conventions for UDT return value

pointing at the line "FP ans = {f};"

I have seen that error before and it usually means that it cannot
convert a thiscall to a non-thiscall. And I agree with the compiler (see
above)
Jul 22 '05 #9
John Doe wrote:
A workaround is to use an intermediate structure:
struct FP {
FP (*fp)(void);
};

FP f(void)
{
FP ans = {f};
return ans;
}
I guess it is because in the implementation of f you are putting a
pointer to a global function, while the definition of FP requires a
__thiscall function pointer (a function member of a class, in which
"this" is passed secretely). I'm NOT sure though.


The sample above does not involve any member functions, just
a data member that is of type pointer-to-function.


Really?
If you do

FP fp;
//....
*(fp.f)();


This will not compile. Should be

(*fp.fp)();

or simply

fp.fp();
you think that the f function called would not get a this pointer?
I think it would!


No, it wouldn't. 'FP::fp' is declared as a pointer to ordinary
(non-member) function. It has nothing to do with any 'this'
Please post the exact code you tried and associated error message.
The snippet is 'kosher' as far as I can tell (although it is not
an example of clarity...).


Exactly your code.
The error I get is:

error C2440: 'initializing' : cannot convert from 'FP (__cdecl *)(void)'
to 'FP (__cdecl *)(void)'
Incompatible calling conventions for UDT return value

pointing at the line "FP ans = {f};"


Interesting. I don't get anything laike this even though I seem to be
using the same compiler as you.

Seeing that two lines of code that you posted above already contain some
errors, I wouldn't be surprised if it turned out that you tested one
piece of code and thought about another.

--
Best regards,
Andrey Tarasevich

Jul 22 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

7
by: Brian Sabolik | last post by:
I'm not sure if I've broken any Object Oriented rules or not, but ... I have projects in 2 different solutions that need to use each other's methods. Therefore I may have an "update" method in...
3
by: Dennis Lerche | last post by:
Hi I have a problem regarding cyclic dependency, yeahh I know bad design. But right at this moment I can't see how it should be redesigned to avoid this. The problem is that I just can't get it...
3
by: Leslaw Bieniasz | last post by:
Cracow, 20.09.2004 Hi, I am posting this again, because my previous message dated 18.09. disappeared from the list (someone has cancelled it ? why ??). I have a problem with compiling the...
10
by: Chris Gordon-Smith | last post by:
I am currently revisiting the code of a C++ application and restructuring the code where appropriate to make it consistent with the overall logical design. As part of this, I am looking at the...
8
by: Christian Christmann | last post by:
Hi, I've a problem with including header files. class A requires header file of class B class B requires header file of class C class C requires header file of class A As can be seen the...
10
by: Ben Taylor | last post by:
Hi, Coming from VB, I've still not really grasped the way how in C++ if function A wants to call function B, then function B has to be before function A in the compilation, and #includes that use...
3
by: fc2004 | last post by:
Hi, Is there any tools that could report where cyclic header dependency happens? this would be useful when working with a large project where tens or hundreds of headers files may form complex...
10
by: toton | last post by:
Hi I have a class called Session, which stores a vector of Page, like vector<PageAlso each Page need's to know the Session to which it is attached. Thus I have, (the classes has many other...
6
by: MaiyaHolliday | last post by:
Hello, I've recently installed apache on a new computer, and cannot figure out why my site will not process any includes. (it was working on my old one) There are no errors on the page such as...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.