OOP SW design is usually compose of one main control routine (henceforth MCR) and lots of sub modules.
But, MCR don't need to (must not need to) know inside implementation of every sub modules.
Sometimes, MCR don't need to know even existence of some sub modules.
The problem is, most sub modules require initialization.
How can sub modules whose existence is now known, be initialized.
Due to this issue, principle of information hiding is sometimes broken.
Let's see below example.
FILE : main.c ------------- int main(int argc, char* argv[]) { ... } FILE : moduleA.c ---------------- ... FILE : moduleB.c ---------------- ...
Assume that, each module requires initialization and main.c don't need to know existence of each module.
How can we resolve this issue?
Easiest way is calling initialization function of each module with damaging principle of information hiding little bit, like mentioned above.
FILE : main.c ------------- extern void moduleA_init(); extern void moduleB_init(); int main(int argc, char* argv[]) { ... moduleA_init(); moduleB_init(); ... } FILE : moduleA.c ---------------- ... void moduleA_init() { ... } FILE : moduleB.c ---------------- ... void moduleB_init() { ... }
At above code, main.c becomes to know existence of moduleA and moduleB.
That is, in terms of modules, principle of information hiding is damaged although it's very little.
Additionally, global symbol space is dirtier.
Regarding maintenance, whenever new module is added, modifying main.c is unavoidable.
But, main.c doesn't have any dependency on newly added module.
With this and that, this way is uncomfortable.
How can we clean up these?
Using constructor leads us to better way.
Functions in constructor are executed before main function.
So, it is very useful for this case.
Easiest way is setting every initialization function as constructor.
But, in this case, we cannot control the moment when module is initialized at.
Therefore, it is better that each module's initialization function is registered to MCR, and MCR calls these registered function at right moment.
Following pseudo code is simple implementation of this concept.
FILE : main.c ------------- void register_initfn(void (*fn)()) { list_add(initfn_list, fn); } int main(int argc, char* argv[]) { ... /* initialize modules */ foreach(initfn_list, fn) (*fn)(); ... } FILE : module.h --------------- extern void register_initfn(void (*fn)()); #define MODULE_INITFN(fn) \ static void __##fn##__() __attribute__ ((constructor)); \ static void __##fn##__() { register_initfn(&fn); } FILE : moduleA.c ---------------- ... #include "module.h" ... static void _myinit() { ... } MODULE_INITFN(_myinit) FILE : moduleB.c ---------------- ... #include "module.h" ... static void _myinit() { ... } MODULE_INITFN(_myinit)
Now, MCR don't need to know existence of each modules.
And, MCR can also control the moment of each module's initialization.
In addition, adding new module doesn't require any modification of MCR side.
It is closer to OOP's concept, isn't it?
We can improve this concept by customizing memory section.
Here is rough description of this.
* Declare special memory section for initializer function. - In gcc, ld script should be modified. * Put initializer function into this section. - __attribute__ ((__section__("xxxx"))) can be used. * MCR can read this section and call these functions at appropriate moment.
Actually, this way is better than using constructor in terms of SW design.
Linux kernel uses this concept in it's driver model.
(For deeper analysis, kernel source code can be good reference.)
But, in many cases, using this concept may lead to over-engineering.
So, if there isn't any other concern, using constructor is enough.
'Language > C&C++' 카테고리의 다른 글
[C/C++] Fail to build Android with g++/gcc-4.6 (Ubuntu-11.10) (0) | 2011.10.17 |
---|---|
[C/C++] func() vs. func(void) (0) | 2011.07.27 |
[Linux][C/C++] strange pipe issue in linux in 'ylisp'. (0) | 2011.02.09 |
[C/C++][Linux] Avoiding making zombie process (0) | 2010.12.14 |
[C/C++][Linux] Tips about LD_PRELOAD (0) | 2010.12.13 |