// Macros for correct module ordering.

#ifndef _CL_MODULES_H
#define _CL_MODULES_H

// The order of initialization of different compilation units is not
// specified in C++. AIX 4 has a linker which apparently does order
// the modules according to dependencies, so that low-level modules
// will be initialized earlier than the high-level modules which depend
// on them. I have a patch for GNU ld that does the same thing.
//
// But for now, I take a half-automatic approach to the correct module
// ordering problem: PROVIDE/REQUIRE, as in Common Lisp.
//
// CL_PROVIDE(module) must be the first code-generating entity in a module.
// Inline function definitions can precede it, but global variable/function/
// class definitions may not precede it.
// Afterwards, any number of CL_REQUIRE(othermodule) is allowed.
// At the end of the module, there must be a corresponding
// CL_PROVIDE_END(module). (Sorry for this, it's really needed.)
//
// These macros work only with g++, and only in optimizing mode. But who
// wants to use CLN with other C++ compilers anyway...

// How to apply these macros:
// 1. Find out about variables which need to be initialized.
//    On Linux/ELF, you can use a command like
//    $ nm -o libcln.a | grep -v ' [UTtRrW] ' | sort +1
//    A symbol of type "D" or "d" lies in the preinitialized DATA section,
//    a symbol of type "B" or "b" lies in the uninitialized BSS section.
//    All of them have to be checked.
//  - Those which contain POD (= plain old data, i.e. scalar values or
//    class instances without nontrivial constructors) are already fully
//    initialized by the linker and can be discarded from these considerations.
//  - Those which are static variables inside a function (you recognize
//    them: g++ appends a dot and a number to their name) are initialized
//    the first time the function is entered. They can be discarded from
//    our considerations as well.
// 2. Find out which of these variables are publically exposed (to the user of
//    the library) through the library's include files, either directly or
//    through inline functions, or indirectly through normal function calls.
//    These variables can be referenced from any user module U, hence any
//    such module must CL_REQUIRE(M) the variable's definition module M.
//    Since there is no CL_REQUIRE_IF_NEEDED(M) macro (which is equivalent
//    to CL_REQUIRE(M) if the required module will be part of the executable
//    but does nothing if M is not used), we must preventively put the
//    CL_REQUIRE(M) into the header file. Hopefully M is either used anyway
//    or does not bring in too much code into the executable.
// 3. Variables which are not publicly exposed but used internally by the
//    library can be handled by adding a CL_REQUIRE in all the library's
//    modules which directly or indirectly use the variable.
// 4. Variables and functions which can be reasonably assumed to not be
//    accessed or executed during initialization need not be treated.
//    For example, I/O to external streams, exception handling facilities,
//    number theory stuff, etc.

// OK, stop reading here, because it's getting obscene.

#if defined(__GNUC__) && defined(__OPTIMIZE__)
  #ifdef ASM_UNDERSCORE
    #define ASM_UNDERSCORE_PREFIX "_"
  #else
    #define ASM_UNDERSCORE_PREFIX ""
  #endif
  #define CL_PROVIDE(module)  \
    extern "C" void cl_module__##module##__firstglobalfun () {}		\
    extern "C" void cl_module__##module##__ctorend (void);		\
    extern "C" void cl_module__##module##__dtorend (void);		\
    struct cl_module__##module##__controller {				\
      int counter;							\
      inline cl_module__##module##__controller ()			\
        { if (counter++)						\
            { goto * (void*) cl_module__##module##__ctorend; }		\
        }								\
      inline ~cl_module__##module##__controller ()			\
        { __asm__ __volatile__ ("\n" ASM_UNDERSCORE_PREFIX "cl_module__" #module "__dtorend" ":"); } \
    };									\
    static cl_module__##module##__controller cl_module__##module##__ctordummy;
  #define CL_PROVIDE_END(module)  \
    struct cl_module__##module##__destroyer {				\
      inline cl_module__##module##__destroyer ()			\
        { __asm__ __volatile__ ("\n" ASM_UNDERSCORE_PREFIX "cl_module__" #module "__ctorend" ":"); } \
      inline ~cl_module__##module##__destroyer ()			\
        { if (--cl_module__##module##__ctordummy.counter)		\
            { goto * (void*) cl_module__##module##__dtorend; }		\
        }								\
    };									\
    static cl_module__##module##__destroyer cl_module__##module##__dtordummy;
  #define CL_REQUIRE(module)  \
    extern "C" void cl_module__##module##__ctor (void)			\
      __asm__ (ASM_UNDERSCORE_PREFIX CL_GLOBAL_CONSTRUCTOR_PREFIX	\
               "cl_module__" #module "__firstglobalfun");		\
    extern "C" void cl_module__##module##__dtor (void)			\
      __asm__ (ASM_UNDERSCORE_PREFIX CL_GLOBAL_DESTRUCTOR_PREFIX	\
               "cl_module__" #module "__firstglobalfun");		\
    struct _CL_REQUIRE_CLASSNAME(module,__LINE__) {			\
      inline _CL_REQUIRE_CLASSNAME(module,__LINE__) ()			\
        { cl_module__##module##__ctor (); }				\
      inline ~_CL_REQUIRE_CLASSNAME(module,__LINE__) ()			\
        { cl_module__##module##__dtor (); }				\
    };									\
    static _CL_REQUIRE_CLASSNAME(module,__LINE__)			\
      _CL_REQUIRE_CLASSNAME(module##_requirer,__LINE__);
  #define _CL_REQUIRE_CLASSNAME(module,line) __CL_REQUIRE_CLASSNAME(module,line)
  #define __CL_REQUIRE_CLASSNAME(module,line) cl_module__##module##__##line
#else
  #define CL_PROVIDE(module)
  #define CL_PROVIDE_END(module)
  #define CL_REQUIRE(module)
#endif

#endif /* _CL_MODULES_H */
