www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - What sorts of things cause cyclic dependencies?

reply Jonathan M Davis <jmdavisProg gmx.com> writes:
Okay. in the code that I'm working on at the moment, I get an exception saying 
that a cyclic dependency was detected but no information whatsoever as to where 
it is or what it means. I haven't been able to find much information on them 
other than some discussions of making it so that the compiler detects them at 
compile time. I have yet to figure out _what_ it is that is causes such errors. 
Could someone clue me in? It's really hard to track down a bug when the error 
only tells you that there's a bug and doesn't say anything about where it 
happens, and I have no clue what sort of things can be cyclically dependent.

- Jonathan M Davis
Oct 13 2010
next sibling parent reply "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Wed, 13 Oct 2010 21:25:15 -0700, Jonathan M Davis wrote:

 Okay. in the code that I'm working on at the moment, I get an exception
 saying that a cyclic dependency was detected but no information
 whatsoever as to where it is or what it means. I haven't been able to
 find much information on them other than some discussions of making it
 so that the compiler detects them at compile time. I have yet to figure
 out _what_ it is that is causes such errors. Could someone clue me in?
 It's really hard to track down a bug when the error only tells you that
 there's a bug and doesn't say anything about where it happens, and I
 have no clue what sort of things can be cyclically dependent.

Say you have two modules, a and b. Both have static constructors, and a imports b: // a.d module a; import b; static this() { ... } // b.d module b; static this() { ... } The language is then defined so that b's constructor is run before a's, since a depends on b. But what happens if b imports a as well? There is no way to determine which constructor to run first, so the runtime throws a "cyclic dependency" exception. The way to fix it is to move one of the module constructors into a separate module: module a; import a_init; import b; module a_init; static this() { ... } module b; import a; static this() { ... } For a "real" example of how this is done, check out std.stdio and std.stdiobase in Phobos. -Lars
Oct 13 2010
parent bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 Well, just knowing that it involves module constructors is definitely useful, 
 though the error message really needs to be improved. Thanks.

You may file a bug report, with an example, asking for a better error message (with a better wording), or a much better error message (that lists the cycle too). Bye, bearophile
Oct 14 2010
prev sibling next sibling parent "Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> writes:
On Thu, 14 Oct 2010 05:53:51 +0000, Lars T. Kyllingstad wrote:

 On Wed, 13 Oct 2010 21:25:15 -0700, Jonathan M Davis wrote:
 
 Okay. in the code that I'm working on at the moment, I get an exception
 saying that a cyclic dependency was detected but no information
 whatsoever as to where it is or what it means. I haven't been able to
 find much information on them other than some discussions of making it
 so that the compiler detects them at compile time. I have yet to figure
 out _what_ it is that is causes such errors. Could someone clue me in?
 It's really hard to track down a bug when the error only tells you that
 there's a bug and doesn't say anything about where it happens, and I
 have no clue what sort of things can be cyclically dependent.

Say you have two modules, a and b. Both have static constructors, and a imports b: // a.d module a; import b; static this() { ... } // b.d module b; static this() { ... } The language is then defined so that b's constructor is run before a's, since a depends on b. But what happens if b imports a as well? There is no way to determine which constructor to run first, so the runtime throws a "cyclic dependency" exception.

I should mention that this can happen with larger cycles as well, i.e. "a imports b, b imports c, c imports ..., ... imports a". This can make some cyclic dependencies very hard to track down. There was a discussion about this on the Phobos mailing list a few months ago: http://lists.puremagic.com/pipermail/phobos/2010-June/thread.html#949 -Lars
Oct 13 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Wednesday 13 October 2010 23:03:05 Lars T. Kyllingstad wrote:
 On Thu, 14 Oct 2010 05:53:51 +0000, Lars T. Kyllingstad wrote:
 On Wed, 13 Oct 2010 21:25:15 -0700, Jonathan M Davis wrote:
 Okay. in the code that I'm working on at the moment, I get an exception
 saying that a cyclic dependency was detected but no information
 whatsoever as to where it is or what it means. I haven't been able to
 find much information on them other than some discussions of making it
 so that the compiler detects them at compile time. I have yet to figure
 out _what_ it is that is causes such errors. Could someone clue me in?
 It's really hard to track down a bug when the error only tells you that
 there's a bug and doesn't say anything about where it happens, and I
 have no clue what sort of things can be cyclically dependent.

Say you have two modules, a and b. Both have static constructors, and a imports b: // a.d module a; import b; static this() { ... } // b.d module b; static this() { ... } The language is then defined so that b's constructor is run before a's, since a depends on b. But what happens if b imports a as well? There is no way to determine which constructor to run first, so the runtime throws a "cyclic dependency" exception.

I should mention that this can happen with larger cycles as well, i.e. "a imports b, b imports c, c imports ..., ... imports a". This can make some cyclic dependencies very hard to track down. There was a discussion about this on the Phobos mailing list a few months ago: http://lists.puremagic.com/pipermail/phobos/2010-June/thread.html#949 -Lars

Well, just knowing that it involves module constructors is definitely useful, though the error message really needs to be improved. Thanks. - Jonathan M Davis
Oct 13 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 14 Oct 2010 02:31:23 -0400, Jonathan M Davis <jmdavisProg gmx.com>  
wrote:

 On Wednesday 13 October 2010 23:03:05 Lars T. Kyllingstad wrote:
 On Thu, 14 Oct 2010 05:53:51 +0000, Lars T. Kyllingstad wrote:
 On Wed, 13 Oct 2010 21:25:15 -0700, Jonathan M Davis wrote:
 Okay. in the code that I'm working on at the moment, I get an  


 saying that a cyclic dependency was detected but no information
 whatsoever as to where it is or what it means. I haven't been able to
 find much information on them other than some discussions of making  


 so that the compiler detects them at compile time. I have yet to  


 out _what_ it is that is causes such errors. Could someone clue me  


 It's really hard to track down a bug when the error only tells you  


 there's a bug and doesn't say anything about where it happens, and I
 have no clue what sort of things can be cyclically dependent.

Say you have two modules, a and b. Both have static constructors,

 imports b:
   // a.d
   module a;
   import b;
   static this() { ... }

   // b.d
   module b;
   static this() { ... }

 The language is then defined so that b's constructor is run before  

 since a depends on b.  But what happens if b imports a as well?  There
 is no way to determine which constructor to run first, so the runtime
 throws a "cyclic dependency" exception.

I should mention that this can happen with larger cycles as well, i.e. "a imports b, b imports c, c imports ..., ... imports a". This can make some cyclic dependencies very hard to track down. There was a discussion about this on the Phobos mailing list a few months ago: http://lists.puremagic.com/pipermail/phobos/2010-June/thread.html#949 -Lars

Well, just knowing that it involves module constructors is definitely useful, though the error message really needs to be improved. Thanks.

I had mostly implemented a cyclic dependency improvement that actually printed the cycle. However, I never committed it because I found there was a huge hole in the algorithm that needed to be addressed. Essentially certain cycles are allowed (even today). I think it can be done with an O(n^2) space and an O(n) algorithm, but I need to work on it. BTW, there is a bug report for this problem: http://d.puremagic.com/issues/show_bug.cgi?id=4384 -Steve
Oct 14 2010
prev sibling parent spir <denis.spir gmail.com> writes:
On Thu, 14 Oct 2010 06:03:05 +0000 (UTC)
"Lars T. Kyllingstad" <public kyllingen.NOSPAMnet> wrote:

 On Thu, 14 Oct 2010 05:53:51 +0000, Lars T. Kyllingstad wrote:
=20
 On Wed, 13 Oct 2010 21:25:15 -0700, Jonathan M Davis wrote:
=20
 Okay. in the code that I'm working on at the moment, I get an exception
 saying that a cyclic dependency was detected but no information
 whatsoever as to where it is or what it means. I haven't been able to
 find much information on them other than some discussions of making it
 so that the compiler detects them at compile time. I have yet to figure
 out _what_ it is that is causes such errors. Could someone clue me in?
 It's really hard to track down a bug when the error only tells you that
 there's a bug and doesn't say anything about where it happens, and I
 have no clue what sort of things can be cyclically dependent.

=20 Say you have two modules, a and b. Both have static constructors, and a imports b: =20 // a.d module a; import b; static this() { ... } =20 // b.d module b; static this() { ... } =20 The language is then defined so that b's constructor is run before a's, since a depends on b. But what happens if b imports a as well? There is no way to determine which constructor to run first, so the runtime throws a "cyclic dependency" exception.

=20 I should mention that this can happen with larger cycles as well, i.e. "a=

 imports b, b imports c, c imports ..., ... imports a".  This can make=20
 some cyclic dependencies very hard to track down.
=20
 There was a discussion about this on the Phobos mailing list a few months=

 ago:
=20
   http://lists.puremagic.com/pipermail/phobos/2010-June/thread.html#949
=20
 -Lars

I'm totally new to D, but have some real-life experience with circular impo= rts. A typical case is with unit testing. Say you design a parsing lib, wit= h (among others) a module defining Pattern types and another for (parse tre= e) Nodes, wich are thee results of successful pattern matches. To test Patterns, no issue, since it already imports Nodes for normal proce= ss. But testing Nodes introduces a circular dependance, only for testing, b= ecause the ordinary way to produce a node, and thus to be able to test its = functionality, is to create a Pattern and run its match method against sour= ce text. 2 solutions I know of: * create fake Pattern types inside the Nodes module, just for testing * export Nodes' test routines into a third module that imports both Pattern= s and Nodes, but is not import. That's probably why many libraries have loads of separated test* unittest m= odules. But I prefere to have tests locally written in the module itself (w= hich well seems to be the intention of D's testing features). Some static languages (eg freepascal) allow mutual dependance as long as at= least one module (M1) uses imported elements of the other (M2) on the impl= ementation side only (not in the interface). This allows the compiler initi= ally constructing the interface of M1 (without any mention of M2), to be im= ported by M2. Then, M2 can be fully constructed, and imported into M1 for t= he needs of its implementation only. Denis=20 -- -- -- -- -- -- -- vit esse estrany =E2=98=A3 spir.wikidot.com
Oct 14 2010