Chic documentation
Houlder Technologies


Tutorial 4: Dependency lookup
Home
Features
Tutorial
Examples
FAQ
Reference
Download
Mailing list
Contact

In this tutorial we look closer at how Chic finds the dependencies and what it puts in the header file that's generated from a hic-file.

As a general rule Chic generates the absolute minimum of declarations and include directives needed to produce self-consistent headers. Indeed, it's not unlikely that you experience both reduced compile times and fewer "dangling dependencies" (declarations and include directives that are no longer needed) when using Chic. The basic lookup was illustrated in the first tutorial, here we will look at it in more detail.

What does Chic look up?

Chic looks up all types that are necessary to compile the header file. Here are some examples of what Chic considers.

  struct S
  {
    // Qualifiers.
    N0::A0* a;
    // Template arguments.
    A1<B0>* a;
    A2 f<B2>();
    // Return types, arguments and exception specifications.
    Ret f0(Arg a) throw (Ex);
    // Function pointers.
    Ret (*fp)(Arg a);
    Ret (Object:: *mfp)(Arg a);
    // Member data.
    Type Object::* a;
    // Catch clauses.
    Ret f1()
    try {
      // ...
    }
    catch (Ex) {
    }
  };
Chic naturally also handles combinations of all the above.

Scoped lookup

Chic keeps track of the scope in which it finds each declaration. If there are multiple declarations with the same name but in different scopes, Chic uses the normal C++ rules to decide which one to use, for example:

typedef int A; // Introduces ::A
namespace N
{
  class A {}; // Introduces ::N::A
}

A a1; // Looks up to ::A
::A a2; // Looks up to ::A
N::A a3; // Looks up to ::N::A
::N::A a4; // Looks up to ::N::A
namespace N
{
  A a5; // Looks up to ::N::A
  ::A a6; // Looks up to ::A
  N::A a7; // Looks up to ::N::A
  ::N::A a8; // Looks up to ::N::A
}

You can introduce new names with using directives or using declarations. Within a hic-file, Chic uses them according to the C++ rules.

File file1.hh:

namespace N1
{
  struct A {};
}
namespace N2
{
  struct A {};
  struct B {};
}

File file2.hic:
namespace N2
{
  using namespace N1;
  using N2::B;
  // Looks up to N1::A through the using directive.
  A a;
  // Looks up to N2::B through the using declaration.
  B b;
}
The C++ lookup rules for using directives are somewhat special. Please refer to the technical reference for details.

Multiple definitions

When there are multiple definitions of a type in the same scope there is no way Chic can decide which one to use. Chic will thus not include or declare anything when it match a multiply defined entity.

Although having multiple definitions is illegal in a linked C++ program it is not unusual that the full code base contains multiple definitions and that the so-called one-definition rule is satisfied by diligent use of include directives. We'll see below how to use sections to cope with this.

Remember that Chic has a dynamic database. Whenever you change a file and update the database, Chic adjusts it's view of the project. So if you should inadvertently add a multiple definition it's just to remove it and update that file.

Typedefs

Chic tracks typedefs and generates all the necessary declarations and include directives to ensure the typedef compiles. If Chic has parsed

File file1.hh:

  namespace N1
  {
    class A {};
  }
  namespace N2
  {
    typedef N1::A Type;
  }
we can generate compile files from

File file2.hic:

  struct C
  {
    N2::Type* t;
  }
and Chic will insert the following in the header

File file2.hh:

  // ...
  namespace N1
  {
    class A;
  }
  namespace N2
  {
    typedef N1::A Type;
  }
  // ...

Note that Chic must be able to track all typedefs uniquely in order to produce any forward declaration or include directive. For example if Chic has parsed the statements

  typedef D Type1;
  typedef Type1 Type2;
it will only output declarations for the statement
  Type2 t2;
if it also can uniquely look up D.

Templates

Chic does not yet completely instantiate templates, so for the hic-file with the contents

  struct C
  {
    Vector<D> vec;
  };
Chic only generates a forward declaration for D even if it may be that the instantiation of Vector requires the full definition of D.

When Chic encounters template defaults it includes the necessary default declarations. In the following code, if the definition for class A is needed Chic includes both file1.hh and file2.hh.

File file1.hh:

  template <class T = int>
  class A;

File file2.hh:
  template <class T>
  class A
  {
    // ...
  };

Chic does not yet resolve template specializations. Either you can use sections or you can put all the specializations in the same file as the template default.

Using sections where lookup fails

When Chic cannot generate all the necessary declarations or include directives you must use sections. Basically, you put the necessary declaration or include directive in the hic-file and on the preceding line you put

  //chic hh
This causes the declaration or the include directive to be raw-copied to the header. For example, if MultiplyDefinedType is defined in the mytype.hh and yourtype.hh files you can choose the mytype.hh version as follows:
  //chic hh
  #include "mytype.hh"

  struct C
  {
    MultiplyDefinedType mdt;
  };

Previous Up




Home