====== C++ style tips ====== ===== Modular architecture ===== It is a very good thing to make a modular architecture. It permits creating several implementation for one functionnality (several ways to do it), and to easily switch from one to the other, or change a part of the program. ==== Inheritance ==== The most obvious way to do that is inheritance. You create an interface, which defines what your implementation must be able to do, and you inheritate several implementations that do it with different ways. Then anywhere else you always use the interface class, and it will use the implementation you initialized with, no matter what it is. Let's take the example of a class that must tell if a pixel belongs or not to a category of colors : class ColorClass { public: virtual bool belongs(Pixel pix) = 0; }; class ColorClassRGB : public ColorClass { int minLum; double minCos; public: ColorClassRGB(int minLum, double minCos); virtual bool belongs(Pixel pix); }; class ColorClassHSV : public ColorClass { int minH, maxH; public: ColorClassHSV(int minH, int maxH); virtual bool belongs(Pixel pix); }; class Vision { public: void init(); void use(Image &img, ColorClass *color); }; Vision vision; void Vision::init() { // ColorClass *color = new ColorClassRGB(20, 0.99); // one ColorClass *color = new ColorClassHSV(20, 50); // or the other use(img, color); } void Vison::use(Image &img, ColorClass *color) { for(int i = 0; i < img.w; i++) for(int j = 0; j < img.h; j++) if (color->belongs(img[i][j])) img[i][j]=Pixel(0,0,0); } It does the job, but it is not the best way, because all functions must be virtual, so they cannot be inlined and the code will be very slow if functions are called often. Moreover this does not really respect the philosophy of inheritance. Inheritance represents the relationship "is a kind of". But ''ColorClassRGB'' is not a kind of ''ColorClass'', it is a ''ColorClass'' implementation. Eventually, it does not always work. If you create the ''Animal'' class with a virtual ''clone'' function (that returns an ''Animal'' of course), and a ''Cow'' class which inherits from ''Animal'', you would like that the ''clone'' function of ''Cow'' return a ''Cow'', but you can't (overriden functions must have same signature). Thus your ''clone'' function must return an ''Animal'', and if you want to use it as a ''Cow'' (because you know it is a ''Cow''), you have to cast-down to a ''Cow'', which is painful and absolutely not secure (possible errors at execution time). ==== Templates ==== There is another solution, which consists in using templates. Give the ''ColorClass'' as a template parameter. The use of this class will define what ''ColorClass'' must do (if something is missing, compilation will fail). \\ If you want to write the ''clone'' function, define a ''return_type'' that you will use without to know what it is, and which will be ''Cow'' or ''Dog'' etc.\\ Then there is no need for virtual functions, and you can inline what you want. class ColorClassRGB { int minLum; double minCos; public: ColorClassRGB(int minLum, double minCos); bool belongs(Pixel pix); }; class ColorClassHSV { int minH, maxH; public: ColorClassHSV(int minH, int maxH); bool belongs(Pixel pix); }; template class Vision { public: void init(); void use(Image &img, ColorClass *color); }; //Vision vision; // one Vision vision; // or the other void Vision::init() { // ColorClass *color = new ColorClass(20, 0.99); // one ColorClass *color = new ColorClass(20, 50); // or the other use(img, color); } void Vison::use(Image &img, ColorClass *color) { for(int i = 0; i < img.w; i++) for(int j = 0; j < img.h; j++) if (color->belongs(img[i][j])) img[i][j]=Pixel(0,0,0); } As you can see, the only problem of defining what a class must do by how we use it, is that they should have the same constructor signature, or you will have to modify also the construction to switch between implementations (still, being able to inline functions can be worth it). ===== The ugly and the pretty ===== Different ways to do some stuff. ==== Redo a loop part ==== //Do a loop part again with the same value of the counter.// for(int i = 0; i < n; i++) { if (tab[i] == -1) { swap(tab[i], tab[n-1]); n--; i--; } else do_some_stuff(tab[i]); } It works. But it won't work in some other cases, if your counter is not just an integer but a pointer scanning a list for example. It's ugly. A nicer workaround : bool redo = false; for(int i = 0; i < n; i+=(redo?0:1), redo=false) { if (tab[i] == -1) { swap(tab[i], tab[n-1]); n--; redo = true; } else do_some_stuff(tab[i]); } ==== Super break ==== //Break several nested loops.// for(int i = 0; i < n1; i++) for(int j = 0; j < n2; i++) { if (tab[i][j] == -1) goto break; } break_: A nicer workaround : bool break_ = false; for(int i = 0; i < n1 && !break_; i++) for(int j = 0; j < n2 && !break_; j++) { if (tab[i][j] == -1) { break_ = true; break; } } ==== A finally block ==== //Do a block of code before returning a function, when there are a lot of return statements in this function.// In this case, as there is no ''try {} finally {}'' block in c/c++, I use the solution with ''goto'', because workarounds would be really painful (copy the finally block everywhere, or set a flag to jump next steps). for(int i = 0; i < n; i++) if (tab1[i] = a) goto finally_; for(int i = 0; i < n; i++) if (tab2[i] = a) goto finally_; for(int i = 0; i < n; i++) if (tab3[i] = a) goto finally_; finally_: free(var); return false; ==== Swap code blocks ==== //Change the order of execution of two blocks of code according to a variable (without duplication of code of course !).// There is not really ugly and pretty ways. Ok, a pretty way would be for sure to create two functions : int x_first = 0; if (x_first == 1) { do_for_x(); do_for_y(); } else { do_for_y(); do_for_x(); } But when the blocks are short, I don't like creating functions called only once because it obliges to scroll when reading. You could do it with ''goto'' too : int x_first = 0; int first_done = 0; if (x_first == 1) goto do_for_x; else goto do_for_y; do_for_x: code_for_x... if (first_done == 0) { first_done = true; goto do_for_y; } else goto do_end; do_for_y: code_for_y... if (first_done == 0) { first_done = true; goto do_for_x; } else goto do_end; do_end: But it's really ugly. The solution I prefer is to use a loop : int x_first = 0; for(int k = 0; k <= 1; k++) { if (k == x_first) { code_for_y... } else { code_for_x... } } ===== Copy of abstract objects ===== ==== Clone function ==== You have an abstract class, and several implementations of this class. You have a pointer to the abstract class, but you don't know which implementations it is, and you want to make a copy of this object. The solution is to use a ''clone'' function. class Descriptor { public: virtual Descriptor* clone() = 0; }; class Type1Descriptor: public Descriptor { public: virtual Descriptor* clone() { return new Type1Descriptor(*this); } }; class AnotherClass { Descriptor *my_descriptor; public: AnotherClass(Descriptor *modelDescriptor) { my_descriptor = modelDescriptor->clone(); } }; ==== Factory model ==== If you just want to copy the configuration parameters of the class, you can create a Factory class that will have the same constructor parameters than your class and will store them internally, and that has a ''create'' function that will create a copy of the class with these parameters. class Descriptor { public: virtual void describe() = 0; Descriptor(int param1, int param2): param1(param1), param2(param2) { internalData = 0; } }; class DescriptorFactory { public: virtual Descriptor* create() const = 0; } class Type1Descriptor: public Descriptor { int param1; int param2; int internalData; public: Descriptor(int param1, int param2): param1(param1), param2(param2) { internalData = 0; } virtual void describe() { ... } }; class Type1DescriptorFactory: public DescriptorFactory { int param1; int param2; public: Descriptor(int param1, int param2): param1(param1), param2(param2) {} virtual Descriptor* create() const { return new Type1Descriptor(param1, param2); } } class AnotherClass { Descriptor *my_descriptor; public: AnotherClass(DescriptorFactory *descriptorFactory) { my_descriptor = descriptorFactory->create(); } }; ===== Inherited parameters ===== First see [[cpp-style-tips#Modular architecture]] to see why you should maybe use templates instead of inheritance here. You want two descriptors of same type being able to merge. So you have to declare it in the base class, with the base type, in order to be able to use it in general. But you don't know how to merge two different kinds of descriptors. If you implement a "generic merger" (with base class as parameter) that does nothing, it will be used in priority, even if you overloaded it with inherited class. The only solution is dynamic casting. class Descriptor { public: virtual void merge(Descriptor* desc) = 0; }; class Type1Descriptor { public: virtual void merge(Descriptor* desc) { Type1Descriptor *desc1 = dynamic_cast(desc); if (desc1 == NULL) { std::cout << "Error: cannot merge Type1Descriptor with another type of Descriptor" << std::endl; return; } // do the merging of two Type1Descriptor here } }; ====== C style tips ====== ===== Generic code ===== As there is nothing such as templates in C, it is more difficult to make generic code. But not impossible. There are at least two ways. ==== Use void* ==== Write all your generic code to work with ''void*'' types, and using parameter functions that works with ''void*'' too. Example: typedef struct Queue_ { void **elements; int size; int first, last; } Queue; void pushQueue(Queue *q, void *elt) { if (((q->last+1)%(q->size+1)) == q->first) return; // full q->elements[q->last] = elt; q->last = (q->last+1)%(q->size+1); } Problems: * functions given in parameter cannot be inlined. For the comparison function in a sort for example, it can be critical. * you cannot use members of structures passed by void* (because you don't know it yet...) ==== Use multiple included file ==== Define all the necessary types and operations with macros, as well as a macro to give a different name to functions every time the file is included, use them in your generic code, and define them before including the file. Example: /* sort.h */ void FNAME(insertionSort)(SORT_TYPE *array, int size) { for(int i = 1; i < size; ++i) { SORT_TYPE val = array[i]; int j = i; while(j > 0 && COMPARE(GET_KEY(val), GET_KEY(array[j-1]))) { array[j] = array[j-1]; --j; } array[j] = val; } } /* somefile.c */ #undef FNAME #undef SORT_TYPE #undef KEY_TYPE #undef GET_KEY #undef COMPARE #define FNAME(name) name##_exval #define SORT_TYPE ExampleTmp* #define KEY_TYPE real #define GET_KEY(x) x->val #define COMPARE(a,b) (a < b) #include "sort.h" #undef FNAME #undef SORT_TYPE #undef KEY_TYPE #undef GET_KEY #undef COMPARE #define FNAME(name) name##_exscore #define SORT_TYPE Example* #define KEY_TYPE real #define GET_KEY(x) x->score #define COMPARE(a,b) (a < b) #include "sort.h" insertionSort_exval(...); insertionSort_exscore(...);