关于MFC的MVC一些想法

在写MFC的时候, 很少是一个对话框能够搞定的, 一般都需要多个对话框交互, 如要是DoMudule的对话框也还好, 因为这种对话框一般不用操作父窗口或者与他平级的窗口, 但是如果是类似在一个 PropertySheet或者更复杂时, 就会出现要多个对话框进行交互. 类似下面一种情况:
1
A区域是一个全局区域(如一个树形控件), B区域根据A区域的选择是用来显示不同的对话框, C域可能是一个Log Window, D区域可能是一些全局按钮(它的操作影响到所有会在B区域出现的对话框), 所有在B区域出现的对话框都要在C区域打印log.
还有最重要的一点, 在B区域中设置一个对话框B1后, 下次显示另一个对话框B2时可能会根据B1的设置状态来做不同的显示.试想一下如果B区域有5个对话框B1, B2, B3, B4, B5.而他们的依赖关系如下:
B1 –> B2, B3, B4;
B2 –> B1, B3, B5;
B3 –> B1, B2, B4;

那么仔细看一下就会发现各种循环依赖, 但是我个人对于循环依赖是有洁癖的.
—————————————————————————————
所以在最近的一次MFC项目中我采用如下方法:
首先我抽象出一个模块E, 这个模块是用来处理纯数据, 他对应于B1~B5对所有状态及实现函数. B1~B5可以设置模块E中属于自己的属性, 可以调用属于自己操作, 至于内部状态怎么去切换是E模块的事, E模块可以用C来实现也可以用C++ 的单例/件模式来实现, 这样他们的依赖关系就成了下面这样:
B1 –> E;
B2 –> E;
B3 –> E;
B4 –> E;
B5 –> E;
如果B区域操作想要对于些全局窗口进行操作如C, 那么只在主对话框在OnInitialDialog中设置一下对于C操作的callback函数即可,这样B1~B5在调用属于自己的函数时, 模块E来决定是不是要使用callback来控制区域C, 还有其它B区域中对话框的数据.

可以看到到目前为止把B区域的对话框当作Module的话, 那么模块E就是Control了.

其实显示即View我没有太好的办法:
我从CDialogEx继承了一个CViewDlg出来然后加了void RefreshUI(void), void RefreshUIData(void)两个纯虚函数, 并重写了OnShowWindow函数差在OnShowWindow中判断当这个对话框在显示时调用RefreshUI, 在被隐藏时调用RefreshUIData.
这样所有B区域的对话框全从CViewDlg中派生出来, 强迫所有B区域对话框来实现RefreshUI来从E模块中获得自己的属性并设置相应的UI内容, 实现RefreshUIData在被隐藏时设置自己的属性到模块E中,以便其他模块在ShoWindow时可能会要用到.
到此View也分开了.

这是我目前有想到的惟一解决循环依赖并差不多实现MVC方法的一种实现, 其实可以看到虽然这种思路可以在写MFC时通用,但是其实做法并不通用尤其是View部分的实现如果是其他的UI设计可能要重新设计View部分的机制才行.

32位系统使用文件作为媒介来模拟大于4G内存访问

代码一篇,暂时没发现bug:

  1 // vm_mgr.cpp : Defines the exported functions for the DLL application.
  2 //
  3 
  4 #include "stdafx.h"
  5 
  6 #include <Windows.h>
  7 #include <vector>
  8 #include <assert.h>
  9 #include <tchar.h>
 10 #include "../common/assist.h"
 11 #include"../error_no/error_no.h"
 12 #include "../Log/log.h"
 13 #include "vm_mgr.h"
 14 
 15 #define VM_ADDR_GEN(addr, index)                (assert(index < 16), (((index & 0xfULL) << 60) | addr))
 16 #define VM_START_ADDR                           0x100000000ULL
 17 #define VM_WINDOW_SIZE                          (64 * 1024 * 1024UL)
 18 #define VM_ALIGN_SIZE                           (64 * 1024)
 19 
 20 struct vm_window {
 21         void                    *p;
 22         vm_ptr_t                start;
 23         unsigned long           size;
 24 };
 25 
 26 struct vm_item {
 27         int                 addr_index;
 28         HANDLE              mutext;    
 29         HANDLE              hMap;
 30         vm_ptr_t            vm_start_ptr;
 31         unsigned long long  vm_size;
 32         struct vm_window    window;
 33         TCHAR               file_path[MAX_PATH];
 34 };
 35 
 36 struct {
 37     std::vector<struct vm_item>    tbl;
 38 } vm;
 39 
 40 static int find_empty_index()
 41 {
 42         int addr_index;
 43         int i;
 44 
 45         for (addr_index = 0; ; addr_index++) {
 46                 for (i = 0; i < vm.tbl.size(); i++) {
 47                         if (vm.tbl.at(i).addr_index == addr_index)
 48                                 break;
 49                 }
 50                 assert(addr_index <= 16);
 51                 if (i >= vm.tbl.size())
 52                         return addr_index;
 53         }
 54 }
 55 
 56 vm_ptr_t vm_alloc(unsigned long long size)
 57 {
 58         TCHAR                       tmp_path[MAX_PATH];
 59         TCHAR                       tmp_file_path[MAX_PATH];
 60         int                         err;
 61         struct vm_item              vm_item;
 62 
 63         GetTempPath(ARRAY_SIZE(tmp_path), tmp_path);
 64         GetTempFileName(tmp_path, _T("DediProg"), 0, tmp_file_path);
 65 
 66         HANDLE hFile = CreateFile(
 67                                 tmp_file_path,
 68                                 GENERIC_READ | GENERIC_WRITE,
 69                                 FILE_SHARE_READ | FILE_SHARE_WRITE,
 70                                 NULL,
 71                                 OPEN_ALWAYS,
 72                                 FILE_FLAG_SEQUENTIAL_SCAN,
 73                                 NULL
 74                                 );
 75         if (hFile == NULL) {
 76                 return NULL; 
 77         }
 78 
 79         vm_item.hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, size >> 32, size & 0xffffffff, NULL);
 80         if (vm_item.hMap == NULL) {
 81                 err = GetLastError();
 82                 return -E_ALLOC_MEMORY_FAIL;
 83         }
 84         vm_item.vm_start_ptr = (vm_ptr_t)MapViewOfFile(
 85                 vm_item.hMap,
 86                 FILE_MAP_ALL_ACCESS,
 87                 0 >> 32,
 88                 0 & 0xffffffff,
 89                 min(size, VM_WINDOW_SIZE)
 90                 );
 91  
 92         vm_item.addr_index = find_empty_index();
 93         vm_item.vm_start_ptr = VM_ADDR_GEN(vm_item.vm_start_ptr, vm_item.addr_index);
 94 
 95         assert(vm_item.vm_start_ptr < ((-1) >> 4));
 96 
 97         CloseHandle(hFile);
 98         hFile = NULL;
 99         vm_item.vm_size = size;
100         vm_item.window.size = (unsigned long)min(VM_WINDOW_SIZE, size);
101         vm_item.window.start = vm_item.vm_start_ptr;
102         vm_item.window.p = (unsigned char *)vm_item.vm_start_ptr;
103         vm_item.mutext = CreateMutex(NULL, FALSE, NULL);
104         _tcsncpy(vm_item.file_path, tmp_file_path, ARRAY_SIZE(vm_item.file_path));
105         vm.tbl.push_back(vm_item);
106 
107         return vm_item.vm_start_ptr;
108 }
109 
110 static __inline int in_region(vm_ptr_t ptr, unsigned long long size)
111 {
112         //static int dbg = 0;
113         int i;
114  
115         for (i = 0; i < (int)vm.tbl.size(); i++) {
116                 if ((ptr + size) <= (vm.tbl.at(i).vm_start_ptr + vm.tbl.at(i).vm_size) && (ptr >= vm.tbl.at(i).vm_start_ptr))
117                         return i;
118                 //if (ptr + size > vm.tbl.at(i).vm_start_ptr + vm.tbl.at(i).vm_size)
119                 //        i = 'DEAD';
120                 //if (ptr < vm.tbl.at(i).vm_start_ptr)
121                 //        i = 'INVA';
122         }
123         
124         LOG_BEGIN {
125                 log_add("----------------------------------------------");
126                 log_add("|             Virtual Memory layout          |");
127                 log_add("|--------------------------------------------|");
128                 for (i = 0; i < (int)vm.tbl.size(); i++)
129                 log_add("|window.start_addr:%llx | window.size:%llx   |", vm.tbl.at(i).window.start, vm.tbl.at(i).window.size);
130                 log_add("|--------------------------------------------|");
131                 log_add("|user.ptr:%llx          | user.size:%llx     |", ptr, size);
132                 log_add("----------------------------------------------");
133         } LOG_END;
134         //dbg++;
135         assert(!"Invalid Memory");
136         return -1;
137 }
138 
139 static __inline unsigned char *scroll_window(int vm_index, vm_ptr_t ptr, unsigned long size)
140 {
141         unsigned char           *p;
142         vm_ptr_t            win_start;
143         unsigned long           win_size;
144         unsigned long long      file_offset;
145         unsigned long long      tmp_offset;
146 
147 
148         win_start = vm.tbl.at(vm_index).window.start;
149         win_size = vm.tbl.at(vm_index).window.size;
150 
151         
152         UnmapViewOfFile(vm.tbl.at(vm_index).window.p);
153 
154         win_size = (unsigned long)min(VM_WINDOW_SIZE, size);
155 
156         assert(ptr >= vm.tbl.at(vm_index).vm_start_ptr);
157         file_offset = ptr - vm.tbl.at(vm_index).vm_start_ptr;
158         
159         tmp_offset = file_offset / VM_ALIGN_SIZE * VM_ALIGN_SIZE;
160         tmp_offset = file_offset - tmp_offset;
161         
162         file_offset = file_offset / VM_ALIGN_SIZE * VM_ALIGN_SIZE;
163 
164         size += (unsigned long)tmp_offset;
165 
166         p = (unsigned char *)MapViewOfFile(
167                 vm.tbl.at(vm_index).hMap,
168                 FILE_MAP_ALL_ACCESS,
169                 file_offset >> 32,
170                 file_offset & 0xffffffff,
171                 size
172                 );
173 
174         if (p) {
175                 vm.tbl.at(vm_index).window.start = ptr;
176                 vm.tbl.at(vm_index).window.size = size;
177                 vm.tbl.at(vm_index).window.p = p;
178 
179                 return p + tmp_offset;
180         }
181 
182         return NULL;
183 }
184 
185 int vm_write(vm_ptr_t pointer, const unsigned char *buff, unsigned long long size)
186 {
187         int             ret;
188         unsigned char       *p;
189         unsigned long       len;
190         int index = in_region(pointer, size);
191 
192         ret = 0;
193 
194         if (index == -1) {
195                 ret = -1;
196                 goto end;
197         }
198         WaitForSingleObject(vm.tbl.at(index).mutext, 20000);        /* timeout 10s */
199         while (size) {
200                 len = (unsigned long)min(size, VM_WINDOW_SIZE);
201                 p = scroll_window(index, pointer, len);
202                 if (!p) {
203                         ret = -1;
204                         goto end;
205                 }
206 
207                 memcpy(p, buff, len);
208 
209                 pointer += len;
210                 buff += len;
211                 size -= len;
212         }
213 end:
214         ReleaseMutex(vm.tbl.at(index).mutext);
215         return ret;
216 }
217 
218 
219 int vm_read(unsigned char *buff, vm_ptr_t ptr, unsigned long long size)
220 {
221         unsigned char   *p;
222         unsigned long   len;
223         int index;
224 
225         assert(buff);
226 
227         index = in_region(ptr, size);
228 
229         if (index == -1)
230                 return -E_ALLOC_MEMORY_FAIL;
231 
232         while (size) {
233                 len = (unsigned long)min(size, VM_WINDOW_SIZE);
234                 p = scroll_window(index, ptr, len);
235                 if (!p)
236                         return -1;
237 
238                 memcpy(buff, p, len);
239 
240                 ptr += len;
241                 buff += len;
242                 size -= len;
243         }
244 
245         return 0;
246 }
247 
248 int vm_free(vm_ptr_t ptr)
249 {
250     int i;
251 
252     LOG_BEGIN {
253                 log_add("----------------------------------------------");
254                 log_add("|             Virtual Memory layout          |");
255                 log_add("|--------------------------------------------|");
256                 for (i = 0; i < (int)vm.tbl.size(); i++)
257                 log_add("|window.start_addr:%llx | window.size:%llx   |", vm.tbl.at(i).window.start, vm.tbl.at(i).window.size);
258                 log_add("----------------------------------------------");
259     } LOG_END;
260 
261     for (i = 0; i < (int)vm.tbl.size(); i++) {
262         if (vm.tbl.at(i).vm_start_ptr == ptr) {
263             assert(vm.tbl.at(i).hMap);
264 
265             UnmapViewOfFile(vm.tbl.at(i).window.p);
266             CloseHandle(vm.tbl.at(i).hMap);
267 
268             DeleteFile(vm.tbl.at(i).file_path);
269 
270             vm.tbl.erase(vm.tbl.begin() + i);
271             return 0;
272         }
273     }
274 
275     assert(!"Invalid pointer");
276 
277     return 0;
278 }

 

 

为什么要使用回调函数

  最近在代码中大量使用了callback函数,没有google到大神关于是否要更多或更少的使用callback函数的论述,可能是这个问题太白痴了吧,暂且陈述一下自己的观点。

  假如,有时候我们需要在逻辑上分2层,如下图:

 

 1 |------------------|
 2 |          High Level         |
 3 |------------------|
 4 |          Low Level          |
 5 |------------------|
 6 //如果这样写
 7 int low_do_a(int xx)
 8 {
 9   hgih_do_b(xx);
10 }
11 
12 int hgih_do_b(int xx)
13 {
14   ...
15 }
16 int high_do_a(int)
17 {
18   ...
19   low_do_a(xx);
20   ...
21 }
   那么逻辑上可能是个样子的:
  
  这种逻辑图令我很不爽,因为我可能还需要知道
hgih_do_b是干什么的,层与层之间会有耦合,如HighLevel变了没有high_do_b。
  那么如果以下面的形式加工一下:
 1  int low_do_a(int xx, int (*do_something)(int xx, void *param), void *param)
 2  {
 3      ...
 4       if (do_something)
 5      do_something(xxx, param);
 6      ...
 7  }
 8     
 9  int hgih_do_b(int xx, void *param)
10  {
11      ...
12 }
13  int high_do_a(int)
14 {
15   ...
16    low_do_a(xx, high_do_b, xxx);
17    ...
18  }
    这样一看,虽然实际上还是调用了上层,但是灵活性大增加,上层可以定制某一部分下层的行为,而且逻辑上来看应该是这个样子:
  
  这样一看逻辑就清晰了很多。
  注:这样做由于会有间接调用,因此,会有一些额外的传参与访存开销,对于
MCU代码中对时间要求较高的代码要慎用。

	

通信模型之“透明”

    今天是大牛走后的第一周,大牛走了,理所当然所有的代码变成我维护修改了。由于最开始写的是写一步算一步,现在0.0.1版release后发现代码结构有很多不合理,理所当然要重构。首先重构的当然是影响着整的前后台系统的socket通信。之前socket的设计是这样的:

     前后台系统共用一个socket.dll,使用socket.dll来封装socket的部分。但是今天看代码时发现其实bg(后台程序)与fg(前台程序)分别加载时socket时,socket.dll分别在两个进程中都有一分部没用的数据与代码拷贝,如后台只需要服务端的相关处理部分数据与代码,前台只需要客户端的处理部分与代码,放在一个DLL中逻辑上看起来很不优雅。而且在socket.dll的代码中定义了一套与外部接口相对应的内部通信数据结构,如socket.dll 导出一个s_aa(struct bb *p)样子的接口那么socket.dll中必有一个struct bb_private与之对应,在调用s_aa时其实p所指向的参数是会被转成struct bb_private后才会被发出去。个人感觉这样做,这个socket.dll仅仅是一个库,如果以后增加了一个功能,这个库可能就需要修改很大,不能实现高内聚低耦合的宗旨。

      个人认为优雅的实现应该是这样的:

    SocketClient.dll与SocketServer负责Socket通信的处理,但是这两个DLL并不知道所有Socket之上的东西,他们仅提供一种机制那就是“透明”,将前台(Client)与后台(Server)之间的网络透明掉,让程序的数据与处理在逻辑上如图中虚线那样执行,如FG调用BG的某个功能,Socketxx.DLL并不需要知道任何关于这个功能函数的数据结构,逻辑上看就像是前台直接调用了BG的函数然后得到了BG函数的返回值。这时Socketxx.DLL不需要知道任何调用的细节,这就是机制,如果要加一个FG调BG的功能函数,那么只需要修改FG与BG而不需要同时修改Socketxx.DLL这两个DLL,这才是透明机制。这样一看逻辑上就清晰多了,而且也优雅多了。

状态机

  从毕业到现在都没有写过博客了,一是没什么大的收获,二是这段时间发生了太多事,从入职到这段时间,思想经历了各种斗争。今天终于有了点值得写的东西,就暂时放下思想斗争记录一下新发现吧。

  以前写代码时从来只知道有状态机,但是从来没有用过,这段时间因为代码开的线程太多了,程序吃不消,经高人指点才发现其实可以用状态机来用一个任务来模拟多个相似任务。下面就来分析一下状态机实现多任务的原理。

  一个完整的代码是由数据和代码组成,由同一个函数创建的多个线程在线程切换时仅仅是切换其中的数据共用其中代码。

  可以实现这样一个纯代码函数,这个纯代码函数只根据参数的内容来执行与代码上下文无关,在模拟切换线程时仅切换一下函数参数即可。线程最重要的特点就是没执行完就可以由时钟中断进行切换,归根到底就是每次每个线程只执行一段程序,因此把一个函数分成几个块,每次每个模拟线程执行一块,这样就可以模拟出一个类似时间片轮转的多线程调度算法, 状态机恰好满足上面要求。语言描述总归是无力的,下面请看代码。

 1 #include <stdio.h>
 2 #include <assert.h>
 3 
 4 struct fsm_param {
 5         int next;
 6         char *param;
 7 };
 8 
 9 int fsm(struct fsm_param *param)
10 {
11         switch (param->next) {
12         case 0:
13                 printf("%c", param->param[0]);
14                 param->next = 1;
15                 break;
16         case 1:
17                 printf("%c", param->param[1]);
18                 param->next = 2;
19                 break;
20         case 2:
21                 printf("%c", param->param[2]);
22                 param->next = 3;
23                 break;
24         case 3:
25                 printf("%c", param->param[3]);
26                 param->next = 4;
27                 break;
28         case 4:
29                 printf("%c", param->param[4]);
30                 param->next = 5;
31                 break;
32         case 5:
33                 printf("n");
34                 param->next = 6;
35                 break;
36         default:
37                 assert(!"Never arrive here");
38         }
39         return 1;
40 }
41 
42 int main()
43 {
44         int i;
45         struct fsm_param thread[2];
46 
47         thread[0].next = 0;
48         thread[0].param = "AAAA";
49         thread[1].next = 0;
50         thread[1].param = "BBBB";
51 
52         do {
53                 for (i = 0; i < 2; i++)
54                         fsm(&thread[i]);
55 
56         } while (thread[0].next != 6 && thread[1].next != 6);
57         return 0;
58 }

  啥也不说了上图看效果,是不是和时间片轮转调度算法写出来的多线程一致呢?甚至我们可以更改调度算法哟!再次感叹一句,C语言博大精深啊!

读The C programming language的新发现

1.C语言标准中的各类型长度,及指针长度并没有明确指定是多长,只是有一个长度限定。还有建议字节数,但这绝不应该成为我们写程序时的标准。因此,我们每到一个新平台的话就最好先搞清楚各类型是多长!

2.C语言中char ,signed char,unsigned char,是3种类型,因为char 到底是signed char 还是char依赖于编译器的实现,K&R建议,为了便于移植,如果我们放的是非字符数据的话最好明确指定是signed char 还是unsigned char!

3.隐式转换有算术运算转换,赋值转换

4.如果一个函数的形参类型未知,那么调用函数时要对相应的实参做“整形提升(integer promotions)",除此以外,float类型将被转换为double类型
5.类似*与++这样的一元运算符遵循从右至左的结合顺序
6.数组下标是有符号型的数值
7.指针-指针 = 指针所指的类型的个数,而非地址的距离.
8.标准允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但不充许与指向数组第1个元素之前的那个内存位置的指针进行比较.