GDI + learning notes (9) Sort Algorithm demo with plug-in (GDI + instance in MFC)

Source: Internet
Author: User

Demo of Sorting Algorithm with plug-in


This section uses an example to describe the application of GDI + in MFC. This algorithm demo is actually the beginning of my algorithm series. Since csdn does not have a tree-like directory structure, I have to use a linked list, isn't it? Well, I will not talk much about it. I will start my article today.


(1) Function Description

Our initial functions are as follows:

(1) The exchange and comparison process of sorting algorithms can be automatically displayed through a bar chart.

(2). Be able to use plug-ins for development. That is, after a new algorithm is completed, you only need to complete a plug-in file (we use the dynamic library dll here) and load the plug-in by the main program to execute it, instead of re-compiling the main program.

(3). Ensure the independence of the main program. That is, the plug-in format remains unchanged during the main program.

(4). You can set the sorting scale. That is, the number of orders.

(5) you can pause the demo and perform the previous step or the next step manually.


(2) Plug-in principles

Our plug-in uses a dynamic library. Although many online experts listed the methods for loading dynamic libraries, I personally think that if you have some simple compilation knowledge, you will find that, in fact, whether you load a static library, including header files or other methods to load a dynamic library, the principle is the same.

(1) Explain your dynamic library to the Compiler

Let's tell the compiler some information about the dynamic library. The dynamic library information should include:

1. Call conventions for output functions.

There are the following types of conventions:

_ Stdcall ,__ cdecl ,__ fastcall ,__ thiscall ,__ nakedcall ,__ pascal

Except for the last _ pascal, they are the same. The difference between them and _ pascal is:

A. the Parameter order of the former is from right to left and then stack (I prefer to think that the pop order is from left to right. I understand it. If there is any deviation, please leave a message. Thank you .) The latter is the opposite.

B. The former is used by the caller to clear the stack, and the latter is used to clear the stack after the caller returns the stack.


2. Function address

When using a function, we also need to know the function address.


3. function prototype


With the above three parts, we can successfully call the dynamic library.

(2) Implementation of plug-ins

1. Return an algorithm instance

The purpose of calling the dynamic library here is to return a pointer to an algorithm-class instance. The only external interface of the dynamic library is to achieve this purpose. The following is the interface implementation code:

BOOL WINAPI Plug_CreateObject(void ** pobj){*pobj = new CAlgorithmCls;return *pobj != NULL;}
The dynamic library only needs to output the above function. The WINAPI tells the call Convention for compiling this function. This macro is the same as the _ stdcall macro.

2. interfaces used in the main process

For each algorithm, we need to use this interface to obtain the class instance pointer we need. Before that, we have to load the dynamic library as follows:

PLUG_ST stPs;ZeroMemory(&stPs, sizeof(stPs));stPs.hIns = LoadLibrary(strPlugPath);PFN_Plug_CreateObject pFunc = (PFN_Plug_CreateObject)GetProcAddress(stPs.hIns, "Plug_CreateObject");
This is a strange thing, that is, the structure PLUG_ST. The definition of this structure is as follows:

typedef struct{CPlugBase * pObj;HINSTANCE hIns;}PLUG_ST, * LPPLUG_ST;
Looking at the name, I believe a smart friend has guessed one or two of them. pObj is the base class pointer of the returned algorithm class. We will introduce it in detail later. The other is the module handle of the dynamic library, that is, the return value of LoadLibrary. We can think that it is an ID card of the dynamic library.

If it is loaded successfully, we can call its unique interface to obtain the first member in the struct, that is, the algorithm instance pointer. The Code is as follows:

if (pFunc!=NULL && pFunc((void **)&stPs.pObj)){m_vecPlugs.push_back(stPs);m_comboAlg.InsertString(0, strAlgName);}
At first glance, it seems a bit complicated. In fact, it is very easy to separate them.

First, we use a vector to store all struct. The second sentence in parentheses only inserts an algorithm name into the combo control.

Then, the first part of the condition requires that pFunc be non-Null, and pFunc is the return value of GetProcAddress. If Null is returned, that is to say, we fail to find the output function Plug_CreateObject, naturally, you cannot perform the next operation.

Finally, the final result is to call the function pointer pFunc we just obtained to assign values to the output parameters to obtain the instance pointer of the algorithm.

So far, we have completed the core code of the plug-in.


3. Plug-in Base class

We use a virtual plug-in Base class to require the plug-in implementers to complete the plug-in functions. The code for this virtual class is as follows:

class CPlugBase {public:CPlugBase(){};virtual ~CPlugBase(){;}virtual void SetData(int nCount, int *pData) = 0;virtual void Start() = 0;virtual bool GetNextOp(int &x, int &y, int &op) = 0;virtual bool GetLastOp(int &x, int &y, int &op) = 0;virtual void End() = 0;};

Let's briefly describe the functions of several functions.

SetData: the main program uses this function to provide the plug-in implementers with the sorted capacity and data.

Start, the main program through this function, requires the plug-in implementer, his own sorting operation. Of course, this function can only be performed after data is set.

GetNextOp/GetLastOp: the main program uses these two functions to obtain the content of an operation from the plug-in. x and y are the indexes of the two data to be operated, and op is the operation type, we will temporarily define it as enumeration, as follows:

enum AlgOp{ALG_SWAP = 0,ALG_COMPARE,};

End: the main program uses this function to notify the plug-in that the job can be cleared.


Each operation step is recorded in a struct, which contains the three values in the preceding parameters. The plug-in implementers can use this structure or write it themselves, or even skip the structure. The following is the implementation of struct:

# Pragma pack (push, 4) struct AlgStep {AlgOp op; int nFirstIndex; // the former's exchange index int nNextIndex; // The latter's exchange index AlgStep & operator = (const AlgStep & _ algStep) {op = _ algStep. op; nFirstIndex = _ algStep. nFirstIndex; nNextIndex = _ algStep. nNextIndex; return * this ;};# pragma pack (pop)

There are two important points in this struct.

A. byte alignment. At first, I always got the index value wrong. After debugging, I also found the problem. After modifying the alignment method, it was correct and I did not think about it carefully. After analysis, the alignment method does not have much impact. The cause may be that the previous modification was not re-compiled and the old obj object was used for the link, the Code actually executed does not match the debugging code. After the alignment mode is modified, the recompilation is performed, so there is no problem.

B. The "=" overload is necessary, which involves the deep copy and shallow copy problems. It seems very professional. In fact, it is very simple. It is the default copy function, which only copies the address of the struct instance and does not transmit the specific values of the struct instance. Therefore, it is necessary to reload this function.


In this way, the base class of the plug-in is completed. The role of this base class is to connect the main program and the plug-in subclass. The plug-in implementer implements the corresponding virtual function by reloading the base class, you can use this algorithm demo to demonstrate your own sorting algorithm.


4. Simply implement a sorting algorithm that is not sorting

First, you need to create a dll library project and. h can be included. Of course, you can also rewrite it yourself. When rewriting, you only need to ensure that the Base you write is exactly the same as the form of the original CPlugBase class, that is: function members of a class are in the same form. Here, we add CPlugBase to the custom Include path to be consistent with the main program.

Then, inherit from CPlugBase to implement various pure virtual functions.

(1) SetData: stores the sorting scale and sorting data into the current data member.

void CAlgorithmCls::SetData( int nCount, int *pData ){m_nCount = nCount;m_pData = pData;}
(2) Start: Save some simple data operations. Here we just make some comparisons. If it is a complete sorting algorithm, we need to improve this function. The Code is as follows:

void CAlgorithmCls::Start(){for(int i=0; i<m_nCount-1; i++){AlgStep as;as.nFirstIndex = i;as.nNextIndex = i+1;as.op = ALG_COMPARE;m_algSteps.push_back(as);}m_nCurStep = 0;}

M_nCurStep indicates the current step. During each calculation, we need to clear the current step to 0.

(3) GetNextOp/GetLastOp: these two functions are used to obtain the content of the next or previous operation, and exchange operations are performed in the function. There is no write exchange operation at the moment, but the data pointer and the index of the exchanged data know that this should not be difficult. The Code is as follows:

bool CAlgorithmCls::GetNextOp( int &x, int &y, int &op ){if (m_nCurStep++ < m_algSteps.size()-1){x = m_algSteps[m_nCurStep].nFirstIndex;y = m_algSteps[m_nCurStep].nNextIndex;op = (int)m_algSteps[m_nCurStep].op;if (m_algSteps[m_nCurStep].op == ALG_SWAP){// ...}}int n = sizeof(AlgOp);if (m_nCurStep >= m_algSteps.size()){return false;}return true;}

If true is returned, the sorting is not completed. If false is returned, all sorting operations are completed.

(4) End, here we do not have any data to be cleared for the time being, because we did not allocate data in the class. According to the principle of who allocates and who releases the data, the pData pointer is also handed over to the outside for release.

Finally, change the output file name to the algorithm name. Here we change it to bubble sort. dll.

5. Complete drawing by using GDI +

(1) The initialization of GDI + seems to have been said many times. Repeat it again, including the header file <objbase. h> and header file <gdiplus. h>, use the name control of GdiPlus, and use "# pragma comment (lib," gdiplus. lib ") to load the static library. GDI + initialization. When OnInitDialog, use GdiplusStartup to initialize GDI + and OnDestroy, release the GDI + resources. In OnPaint.

(2) Draw a demo program. Read the code and explain it again.

CDC * pCDC = GetDlgItem (IDC_SHOWPIC)-> GetDC (); HDC hdc = pCDC-> GetSafeHdc (); Graphics grphics (hdc); RECT rect; GetDlgItem (IDC_SHOWPIC) -> GetClientRect (& rect); Bitmap bitmap (rect. right-rect.left, rect. bottom-rect.top); Graphics grp (& bitmap); grp. clear (Color: Black); int nWidth = (rect. right-rect.left)/m_nCount/2; int nOffset = (rect. right-rect.left-nWidth * 2 * m_nCount)/2; // offset int nBottom = 20; for (int I = 0; I <m_nCount; I ++) {RECT box; int nHeight = (rect. bottom-rect.top-nBottom)/m_nCount * (m_pData + I); box. left = nWidth + I * nWidth * 2 + nOffset; box. right = box. left + nWidth; box. bottom = rect. bottom-nBottom; box. top = box. bottom-nHeight; if (I! = M_CurOp.nFirstIndex & I! = M_CurOp.nNextIndex) {SolidBrush sbrush (Color: Crimson); grp. fillRectangle (& sbrush, box. left, box. top, box. right-box.left, nHeight);} else {SolidBrush sbrush (m_Specialcolor); grp. fillRectangle (& sbrush, box. left, box. top, box. right-box.left, nHeight) ;}} grphics. drawImage (& bitmap, 0, 0 );

We first draw a Bitmap in a Bitmap, and then draw the Bitmap content to the graphical control (in fact, it is a Static control of a rectangle, essentially as long as it is a CWnd can be drawn ).

Since we use integers from start to end coordinates, there is a little error in a column and there is no problem, but if there is more, the offset will be very large. We will calculate all the offsets, if the image is evenly distributed to both sides, the final image will not be too aligned.

The calculation of the size of a column is very simple, but it is very wordy. Let's just look at it. I will not explain it too much.

To draw a rectangle in each column, you need to fill in a real painting brush, and then fill in a rectangle with the paint brush to complete the final painting.

The background color, filled with black.

So far, we have finished drawing the original image and simply look at the effect.



6. manually execute the previous step or next step

Each time we draw a chart, we need to record what the current operation is, what the color of the current operation is, get the previous step, and complete the next step. First, let's look at the next step of code:

void CAlgorithmDemoDlg::OnNextStep(){m_Specialcolor = Color::Blue;int nOp;m_vecPlugs[m_nCurAlg].pObj->GetNextOp(m_CurOp.nFirstIndex, m_CurOp.nNextIndex,nOp);m_CurOp.op = (AlgOp)nOp;Invalidate(FALSE);}

Here, we should actually decide the special color based on the Operation Type nOp. Here we define blue directly to save time. In addition, our painting is placed in the OnPaint function. Therefore, when getting the next step, we should tell the program that the graphic box should be refreshed. The most straightforward method is to send an OnPaint message to the program. Here we use Invalidate. This function will send the OnPaint function. FALSE indicates that the message is not erased. If it is TRUE, onPaint does not draw anything. Of course, we can also use the base class Invalidate to redraw a region.

::InvalidateRect(m_hWnd, NULL, bErase);

Currently, we call the above Code. to re-draw a rectangle, you only need to replace NULL with the pointer of the RECT instance you need to re-paint.


7. timer and automatic demonstration

The timer in MFC is very simple. We only need to set a timer at the beginning of the automatic demonstration. After the demo is completed, Kill the timer and then complete the OnTimer message response.

Automatic Message demonstration

void CAlgorithmDemoDlg::OnBnClickedAutorun(){SetTimer(1, 500, NULL);}

OnTimer completes timer response

Void CAlgorithmDemoDlg: OnTimer (UINT_PTR nIDEvent) {// TODO: add the message processing program code and/or call the default int nOp; bool bRet; switch (nIDEvent) {case 1: bRet = m_vecPlugs [m_nCurAlg]. pObj-> GetNextOp (m_CurOp.nFirstIndex, m_CurOp.nNextIndex, nOp); m_CurOp.op = (AlgOp) nOp; if (! BRet) {KillTimer (1); <span style = "white-space: pre"> </span> // The algorithm has been demonstrated or failed, disable Automatic demo} Invalidate (FALSE); break;} CDialog: OnTimer (nIDEvent );}

See the following results:


So far, the demo program has been basically completed, and there are still some details, so I will not talk about it here. (Why is the uploaded image static? Say a good dynamic graph scheme ?... Okay, I am a little lazy recently. I will continue to adjust this article if I have time at the weekend. Now we will see dynamic algorithm demo images and a decent interface document .)


Now, we are about to begin the implementation of sorting algorithms. Currently, we plan to implement ten common sorting algorithms first.






Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.