A GlobalManager
object does some very simple management of (certain)
global values used by CoCoALib (i.e. initialization and eventually
destruction). So that the operations in CoCoALib can work properly you
should create an object of type GlobalManager
before using any other
feature of CoCoALib. Conversely, the GlobalManager
object should be
destroyed only after you have finished using CoCoALib features. An easy
way to achieve this is to create a local variable of type GlobalManager
at the start of a top level procedure (e.g. main
). See the CoCoALib
examples.
Currently the GlobalManager
class has only a default ctor; later it may be
possible to specify some global settings as ctor arguments -- this would be
primarily to aid debugging during development.
An exception will be thrown if you attempt to create more than one
GlobalManager
object (without having destroyed all earlier
GlobalManager
s). The exception is of type CoCoA::ErrorInfo
and has error
code ERR::GlobalManager2
.
The concept of GlobalManager
was created to handle in a clean and
coherent manner (almost) all global values used by CoCoALib; in
particular it was prompted by the decision to make the ring of
integers a global value. The tricky part was ensuring the proper
destruction of RingZ
before main
exits. Recall that C++
normally destroys globals after main has completed, and that the order
of destruction cannot easily be governed; destroying values in the
wrong order can cause to the program to crash just before it
terminates. Another advantage of forcing destruction before main
exits is that it makes debugging very much simpler (e.g. the
MemPool
object inside RingZImpl
will be destroyed, thus
allowing the MemPool
destructor to perform all its checks). And
of course it is simply good manners to clean up properly at the end of
the program.
To implement the restriction that only one GlobalManager
may exist
at any one time, the first instruction in the ctor checks that the
global variable GlobalManager::ourGlobalDataPtr
is null. If it is
null, it is immediately set to point the object being constructed --
it is important that ourGlobalDataPtr
be set before attempting to
construct the rationals!
I chose to use std::auto_ptr
s for the data members because I
needed to have created and "registered" the ring of integers before
attempting to construct the rationals. The excludes initializing a
data member of type FractionField
outside the body of the ctor.
Use of a pointer allows construction of Q to be deferred, and use of an
auto_ptr
makes clean-up automatic. For consistency, I chose to
implement Z using the same mechanism -- it also allows me to be quite
explicit about construction order of data members inside the body of
the ctor for GlobalManager
.
The two functions MakeUniqueCopyOfRingZ
and MakeUniqueCopyOfRingQ
are
supposed to be accessible only to GlobalManager
; they create the unique
copies of those two rings which will be stored in the global data. The
functions are defined in RingZ.C
and RingQ.C
respectively but do not
appear in the corresponding header files (thus supposedly making them
"inaccessible" to other users). Note that the ring representing Z must be
created and made accessible via ourGlobalDataPtr
before
MakeUniqueCopyOfRingQ
is called. I know, this is a bit dirty/delicate;
too bad, I can't see any simple and clean way of achieving what I want.
There is a potential bug in dtor for GlobalManager
: the global values are
deregistered before they are destroyed. Might it be better to use raw ptrs
and call delete
explicitly? -- this would also make the order of destruction
explicit.
Should the ctor for GlobalManager
initialize the CoCoALib i/o streams?
Perhaps the GlobalManager
ctor should accept an argument which would
cause it to create a GMPAllocator
?
You cannot print out a GlobalManager
object; is this really a bug?
Potential race condition, if you try to create two GlobalManager
s
in parallel.
Should the ctor for GlobalManager
set the globals which control
debugging and verbosity in MemPool
s?