About table-driven
The first time I got started with the table driver, I still graduated shortly after graduation. At that time, a department manager explained refactoring to us, namely, the simplified condition expression section in "refactoring: improving the design of existing code". The if statement processing replaces it with the polymorphism form, for example, the factory model. However, even if it is replaced by a factory, the switch or if judgment still cannot be removed. What can be done to solve this problem?
At that time, I was still studying the STL source code and thought of the traits programming technology, which could solve the if judgment problem during the compilation period (although I had this idea, it had never been implemented successfully ). Various Daniel put forward different opinions, and everyone basically agreed to one article: Use a "table" to solve the problem. At that time, I was too knowledgeable and didn't know what it meant until I knew "table-driven ".
"Table-driven" comes from "code Daquan". In my definition, this book is a software engineering book. The table driver appears as a single chapter and is recommended as the first read chapter for junior programmers in the preface.
First, why is table-driven? The purpose of table-driven is to avoid logical statements (if and case) and use tables to find and determine information. So why? As mentioned in Chapter 5.2 of code Daquan, the primary technical mission of software is management complexity. The complexity can be determined by the circle complexity (the number of executable paths of a function), which involves graph theory and other aspects. The use of the table driver can greatly reduce the complexity.
What is the table driver? Anything that can be selected using a logical statement can be selected through a table. For example: Case 1: Select thing 1; Case 2: Select thing 2. The format stored in the table is as follows:
Situation |
Things |
1 |
1 |
2 |
2 |
... |
... |
Then, all statements that use if and case to select things can be replaced with the following forms:
Table [Selected I];
In this way, the corresponding content can be directly obtained through the first address + offset to cancel the judgment logic. Suppose you want to select the nth thing, it is the first address + N, and the nth thing is obtained directly. If you use the normal judgment logic, you may need to judge n times to obtain the nth thing.
For other details, you can go to Baidu ~
Factory Model
The factory model comes from the design model. It is one of the most basic models and one of the most commonly used models. The factory model is also very simple. A simple factory is used to describe the table-driven problem. The UML diagram is as follows:
When creating a product, most of them will go through this process:
Product *product = nullptr;switch(productType){case TYPE_PRODUCT1: product = new(std::nothrow)Product1(); break;case TYPE_PRODUCT2: product = new(std::nothrow)Product2(); break;case TYPE_PRODUCT3: product = new(std::nothrow)Product3(); break;case TYPE_PRODUCT4: product = new(std::nothrow)Product4(); break;default: break;}
The problem arises. How can we replace a logical statement with a table driver?
Function pointer
Before entering the formal topic, there are still some content to solve, because the storage information in the table needs this part of content.
The function pointer must have some knowledge, such as the following code:
// Define a function pointer typedef void (* funcptr) (); // define the function void func () {STD: cout <"func. "<STD: Endl;} int main (INT argc, char ** argv) {// point the function pointer to the corresponding function funcptr PTR = func; // call the function PTR (); Return 0 ;}
Factory table drive
With the product type and product creation method, how can I write it into a table? Generally, we store the data as follows:
Type_product1 |
Create a function pointer of the type_product1 type |
Type_product2 |
Create a function pointer of the type_product2 type |
Type_product3 |
Create a function pointer of the type_product3 type |
Type_product4 |
Create a function pointer of the type_product4 type |
But how should we implement it in C ++? Tables can be implemented using arrays and maps, for example, the following code:
typedef Product* (*NewProduct)(); struct ProductCreator{ int m_productType; NewProduct m_newProductFuncPtr;}; const ProductCreator PRODUCT_CREATOR[] ={ { TYPE_PRODUCT1, newProduct1 }, { TYPE_PRODUCT2, newProduct2 }, { TYPE_PRODUCT3, newProduct3 }, { TYPE_PRODUCT4, newProduct4 },};
But is that okay? Because New creates the actual object and cannot be converted into a function pointer, it is certainly not possible. How can this problem be solved?
Use Function imitation
It is hard to understand, but there is always a solution to the problem. To create different objects of different types, isn't it the idea of a template? From this perspective, the problem will be solved immediately ~
Solution: Use a template to create a function (function object) and create an actual object through the function object. The implementation code is as follows:
typedef Product* (*NewProduct)(); template <class T>struct TypeCreator{ static Product *New() { return(new(std::nothrow) T()); }}; struct ProductCreator{ int m_productType; NewProduct m_newProductFuncPtr;}; const ProductCreator PRODUCT_CREATOR[] ={ { TYPE_PRODUCT1,TypeCreator<Product1>::New }, { TYPE_PRODUCT2,TypeCreator<Product2>::New }, { TYPE_PRODUCT3,TypeCreator<Product3>::New }, { TYPE_PRODUCT4,TypeCreator<Product4>::New },};
In this way, you can query the table product_creator to obtain the function object, and then call its method to obtain the specific object. For example:
Product *product = PRODUCT_CREATOR[i].m_newProductFuncPtr();
Use Pointer to member function
I recently read "Deep Exploration of the C ++ Object Model" and gained a lot. When I saw a pointer to the member function, I had a whimsy and decided to give it a try to see if it could solve this problem.
The pointer to memberfunction, as its name implies, is a pointer to a class member function. In fact, it is similar to a function pointer. Its declaration method is as follows:
class A{public: void Func() {std::cout << "A Func." << std::endl; }}; int main(int argc, char **argv){ void (A::* funcPtr)(); funcPtr =&A::Func; A a; (a.*funcPtr)(); A *b = new A; (b->*funcPtr)(); return 0;}
Seeing such a code, there is a feeling of "no way, no way". Let's take a look at whether the problem can be solved?
The final result failed. There are two reasons:
1. I can't figure out how to write the constructor pointer to the member function. Because the constructor does not return values, the pointer to the member function must have a definition of the return value.
2. Do you still remember what the C ++ instructor said in the first lesson? To be honest, a class automatically generates constructor, destructor, and copy constructor by default. In fact, this is wrong. According to the constructor semantics, a class automatically generates constructor only in the following four situations:
1> If a class does not have any constructor, but one of its members has a default constructor, the class also needs to generate a default constructor, however, this operation only happens when the constructor is called.
2> If the base class contains a default constructor and the subclass does not have any constructor, the default constructor needs to be merged.
3> when a class contains virtual functions, if no constructor is defined, the default constructor needs to be merged.
4> when the class has virtual inheritance, if no constructor is defined, the default constructor needs to be merged.
In fact, the first two points depend on the last two points. Why? Both the first and second points require that the parent class or Members contain the default structure. First, the declared constructor is not called the default structure. Second, since there is a default structure, so it must be caused by the third or fourth point. Therefore, the first and second points depend on the last two points.
Therefore, the following class has no constructor, including the default constructor:
class Product{public: int m_IntVal;};
A pointer to memberfunction pointing to the constructor must fail. Therefore, the compiler prohibits the pointer to the constructor and prompts the following message:
Error: A constructor or destructor may not have its address taken
Further consideration
First of all, the table-driven method is a skill that must be mastered. Using it will improve program efficiency, clean code, and so on.
Second, project management must be defined and controlled based on relevant standards. It is vital to use sourcemonitor and other tools to grasp the quality of the project. In this week's employee training, I was deeply aware of the profound principle of "no silver bullet" after listening to the many years of experience of Lahan. On the premise that "Silver Bullet" was not created, any process must be strictly controlled; otherwise, it will fall into endless tar traps ".
Third, continue to dig a few more traps for yourself. If you continue to go to the ground, it seems that you will never reach the peak, because the road to the peak will never be flat.
Bibliography
Code Daquan Second Edition Steve McConnell
STL source code analysis Hou Jie
Refactoring: improving the design of existing code Martin Fowler
Design Pattern: the basis for reusable object-oriented software gof
Big talk design model Cheng Jie
Deep Exploration C ++ object model Stanley B. Lippman
Mythical man-month Frederick P. Brooks. Jr.