autumn相关日志(1)

从space.live.com上搬过来,担心过两天就没了。

 

关于AOP的实现
准备实现一下C++的AOP,考虑来考虑去,在Wrapper和Proxy两者之间的关系还是没有很想清楚。有两种可能的实现方式:
(1)Proxy直接继承于被代理类,重新实现所有方法,在实现中调用被代理类的方法,相当于静态代理。客户端应用时,直接创建Proxy的实例。如果使用Autumn DI框架,则对Proxy生成Wrapper,进行注入。这种方式的好处是比较简单,可以单独使用而跟Autumn DI框架没有关系结,如果跟Autumn DI框架结合,只要创建Proxy的Wrapper即可。坏处是对于工厂模式(包括工厂方法和抽象工厂),不对原代码做修改的话,就不能创建Proxy的实例。关系如第一个图。
 
(2)Proxy不仅继承于被代理类,而且持有被代理类的实例,在Proxy的方法实现中,通过实例调用被代理类的相关方法。客户端应用时,要先创建被代理类,然后再创建Proxy。这种方式的好处是被代理类可以使用工厂模式创建。坏处是直接使用时比较麻烦(要创建两次),如果跟Autumn DI框架结合,则需要对Proxy和被代理类都要生成Wrapper,会导致类大量增加(将两个Wrapper合并?)。关系如第二个图。
 
无论哪种方式,Proxy都是自动生成,都是只能对虚函数实现AOP。使用Wrapper来创建Proxy,可能会有些动态代理的效果:只要传入相应的Wrapper,就可以创建出相应的Proxy来。也可能没有必要,因为实际上各个代理类已经存在了,不象JAVA中那样真正地动态创建。对于InvocationHandler这样的机制,应该是实现interceptor即可。Proxy中应该注册各种Interceptor,对所有方法的,对个别方法的,各种切入点的。如果不使用DI框架,就需要在创建Proxy时,人工把被代理类及Interceptor传进去。
 
 
 
 
 
 
又有人关注Autumn了
今天收到一封邮件,又有人关注Autumn框架了,令我很是高兴,呵呵。
他提到了几个问题:
1、名字空间的问题。Autumn没有使用名字空间,这会使它的一些定义及类名可能会跟别的库相同,不便于使用。其实我个人对于名字空间没有很好的应用经验。
2、关于const的非正确使用。这一点没有很看明白,需要再跟他讨教。
3、std::string做参数时,使用的是传值,而不是常量引用。
看来我在编程方面还要不断提高啊!:-)
 
 
关于Autumn Framework的多重继承问题

1、  1、问题描述

Autumn是实现C++依赖注入的一个框架。C++是可以多重继承的,Autumn在处理一个多重继承的注入时存在如下问题。

假设一个类的定义如下:

class  A : public A1, A2
{
       …
};

在把A的实例当作A1进行注入时,可能没有问题;但当作A2进行注入时,并不能将A的指针正确的转换为指向A2的指针。对同一个实例,A的指针和A2的指针会有一定的偏移。  

2、  2、解决思路

  1. a)         运行时动态判断并转换

如果Autumn能在运行时动态判断将要转换成哪个父类的指针,并做正确的处理,是最好的办法。但是C++不支持运行时的动态判断及转换。即使是dynamic_cast也是需要在编译时知道要置换的类型。不能够做到在运行时只提供一个父类的名称,就可以进行正确的转换。

  1. b)        转换在编译时就处理好

即,在编译Wrapper时就知道该类共有哪些父类,于是也就确定了可能会有哪些转换,把这些转换都处理好。在运行时,只要向Wrapper提供父类的名称,就可以选择正确的转换进行处理。这种方式算是比较可行的。

但,问题是,如何知道一个类共有哪些父类呢?按照现在自动生成Wrapper的方式(AutumnGen),很容易知道直接继承的父类有哪些,但间接继承的父类有哪些就很难知道了。而间接继承的父类很可能就是作为注入类型的类。现在考虑有两种方式来知道有哪些父类:

  •                         i.              (1)修改AutumnGen,自动找出一个类的所有父类,包括直接和间接继承的。但实现起来应该比较麻烦,而且也不好判断上溯到多少辈比较合适。
  •                       ii.              (2)在使用AutumnGen时,让开发人员指出一个类的父类有哪些,只需要指出可能作为注入类型的父类即可。但让开发人员做这些事情感觉不是很好:他还要考虑用这个类的人想把它作为什么类型注入。从另方面想,也还可以接受:开发人员开发的这个类,可能作为哪些类型进行注入,开发人员应该清楚。还可以修改一个Autumn,对于直接继承的父类,不需要开发人员再次来指定。

 

 

 

C++0x
今天晚上看了一些关于C++0x的文章,感觉还是不错的,知道了不少东西。:-)
 
 
依赖倒置、控制反转和依赖注入的区分
依赖倒置(Dependency Inversion Principle)、控制反转(Inversion of Control)和依赖注入(Dependency Injection)从思想来讲是统一的,或者说是类似的,有人也说它们是同一个东西。但是还是可以做一点区分:
  • 依赖倒置原则
      是进行软件设计时考虑遵循的一个原则。具体为:
      (1)上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。
      (2)抽象不能依赖于具象,具象依赖于抽象。
  • 控制反转
      是软件运行时体现出来的一个特征:如果对象A运行时依赖于对象B,但A并不去创建B,而是从外界直接取得B。也就是说,一个对象并不是自己去创建它所依赖的其它对象。
  • 依赖注入
      是控制反转的一种实现手段。如上面的例子,B的取得并不需要A的干涉,而是利用某些框架在通过构造参数或属性设置来实现。
 
今天编程所犯的两个错误
就是因为是比较低级的错误,所以太难查原因了,因为自己没想到会是那方面的原因。
第一个错误:把变量作用域外使用了它。因为是将其赋给了另外一个变量,所以,并不太容易发现。代码如下:

 void_ptr* pv;
 if( num > 0 ){ //Has constructor argments
  auto_ptr<void_ptr> pp( new void_ptr[num] );
  pv = pp.get();
 
  for(i=0; i<num; i++){
   // add argument into pw, not pwc
   pv[i] = (*pargs)[i]->takeoutValue(pw.get(), this->ManagerOfType);
  }
 }
 p = pwc->createBean(bc->getConMethodName(), pv, num);

在后面使用pv时,其实它所指向的内容已经被释放掉了。
 
第二个错误:忘记了应有的处理逻辑。一开始思考的时候,是想到用工厂创建的bean应该设置到bean本身的wrapper中,以进行相关操作,但在改代码的时候,忘记了。又浪费了好多时间进行调试。应该及时将思路记录下来。
 
 
虚析构函数与多重继承
看下面的代码及运行结果。当有多重继承时,要想在析构一个子类实例时,能够调用所有父类的析构函数,应将所有父的析构函数声明成虚的。
代码:

#include <iostream>
using namespace std;
 
class baseA{
public:
    ~baseA(){
        cout<<"This is in baseA::~baseA()"<<endl;
    }
};
 
class baseV{
public:
    virtual ~baseV(){
        cout<<"This is in baseV::~baseV()"<<endl;
    }
};
 
class derivedV: public baseA, public baseV{
public:
    ~derivedV(){
        cout<<"This is in derivedV::~derivedV()"<<endl;
    }
};
 
int main()
{
    baseV *p = new derivedV();
    baseA *p1 = new derivedV();
 
    cout<<"Delete baseV:"<<endl;
    delete p;
 
    cout<<"Delete baseA:"<<endl;
    delete p1;
 
    return 0;
}

运行结果:
Delete baseV:
This is in derivedV::~derivedV()
This is in baseV::~baseV()
This is in baseA::~baseA()
Delete baseA:
This is in baseA::~baseA()
虚函数与析构函数
1、虚析构函数的作用
如果父类和子类都有析构函数,又把子类的实例赋给父类的指针,在delete这个指针时,如果父类的析构函数是虚的,则会调用子类的析构函数,否则,只调用父类的析构函数。
2、析构函数中调用虚函数
析构函数中调用虚函数时,所调用的虚函数的实现,是本身这个类的实现,不会是子类的实现。按照析构顺序,先析构子类,再析构父类,所以,在父类的析构函数中,应该不会调用到子类中的实现。
3、例子代码
例子代码如下,可以将父类的析构函数改为非虚的看一下结果有什么不同。

#include <iostream>
 
using namespace std;
 
class baseV{
public:
    virtual void f1(){
        cout<<"This is in baseV::f1()"<<endl;
    }
    virtual ~baseV(){
        f1();
        cout<<"This is in baseV::~baseV()"<<endl;
    }
};
 
class derivedV: public baseV{
public:
    virtual void f1(){
        cout<<"This is in derivedV::f1()"<<endl;
    }
    ~derivedV(){
        f1();
        cout<<"This is in derivedV::~baseV()"<<endl;
    }
};
 
void main()
{
    baseV *p = new derivedV();
    delete p;
}

运行VC8的程序,误用VC6的动态库!
都是粗心惹的祸。
将Autumn用VC8编译后执行测试程序,总是报错,简直是太奇怪了。而且跟踪时还不让监视变量,说是监视不到,弄得自己非常郁闷!什么中间信息也看不到,只知道到达某一个点的时候,突然就出错了。 怀疑是VC8express版本的问题?于是装了个team版,依然如此!
修改代码调试,跟踪过程中打开源文件,发现代码竟然没有修改!前几次都是在跟踪过程中打开文件修改的,修改后进行编译,倒也没看出什么不对头来。只是有时候奇怪自己修改的代码怎么没有保存,或是修改了原来目录下的文件。
今天修改代码再次跟踪时,发现打开的源文件是没有修改的,于是我怀疑调试时装入的动态库不是我刚刚编译的,而是原来目录下VC6编译的,所以跟踪时就打开了原来目录下的源文件。
修改path,让运行时装入新编译的动态库,一切OK!
都是粗心惹的祸啊!
 
Autumn跟Tuscany的实现思路有点儿类似
还没有仔细来看,只是今天学习用Tuscany,发现Autumn的实现思路有一部分跟Tusacny是类似的。
在Autumn中,为了进行注入,对poco进行封装,生成wrapper,供Autumn调用构造方法及属性设置方法。在Tuscany中,也使用了wrapper,而且其中的实现跟Autumn类似,也是判断方法名称。不同的是它用了一个operation来封装所有的参数及返回值,但也用了强制类型转换。在proxy中通过wrapper调用最终实现。在Autumn中没有proxy,是由框架引擎直接调用wrapper的方法。因为Autumn不象一般客户端那样,需要识别实现类提供的方法名,所以不需要proxy。
当然,Tuscany中的wrapper及proxy是用工具生成的,而Autumn的wrapper是通过宏由开发人员自己定义的。
 
Solaris上的make略有不同
为了自动把目标文件列表生成出来,在makefile中写了一点代码,第一个命令是生成每个.o文件所依赖的文件,第二、三个命令是将.o列表赋给变量OBJS,并放到一个文件中。

depend::
 $(CXX) -I../include -MM *.cpp >$(DEPENDFILE)
 @echo "OBJS= //" >$(OBJSFILE)
 @ls *.cpp | sed -e 's/cpp$$/o ///' >>$(OBJSFILE)

我发现生成后的文件的最后一行也带有一个续行符,心里还想会不会不好用。但在ubuntu和aix上都没有问题,我就以为没有妨碍了。没想到在solaris上一执行,便发现OBJS变量值为空。开始我还挺纳闷,后来猜想可能是最后一个续行符的原因。试了下,果然!
只好在makefile中再加一句:
     @echo >>$(OBJSFILE)
适当的日志很有用
再一次体会到在程序中进行适当的日志是很有用处的。
从下午到晚上一直在调试程序,程序出错了,但不知道原因在什么地方,一直以为是新编写的代码出了问题。最后查出来是配置文件写错了。配置文件中的bean名称写成“ProductA1 ”,后面多了一个空格,而程序中取bean的时候名字没有空格,所以取不出来。但是由于没记日志,所以没有发现这个问题,在程序中转了一圈,最后加调试语句才跟踪出来。其实,在取不到bean的相关配置的时候,应该适时地记录一下日志。那就会很快发现该问题的原因了。
是个教训。
 
 
模式-静态工厂方法
原来只知道抽象工厂、工厂方法等创建型模式,听到静态工厂方法后没有深刻理解。原来单例其实就是静态工厂方法啊!“目前比较流行的规范是把静态工厂方法命名为valueOf或getInstance。”
1、valueOf:该方法返回的实例与传给它的参数具有同样的值。如:
Integer a = Integer.valueOf(100);    //返回取值为100的Integer对象
2、getInstance:返回的实例与参数匹配。如:
Calendar cal = Calendar.getInstance(Local.CHINA);    //返回符合中国标准的日历
 
 
 
日志的级别
通常,日志一般分为5级:
debug、info、warning、error和fatal(对应为:调试、信息、警告、错误、致命)。
今天,我在avast!上看到其将日志分为7级:
info、notice、warning、error、critical、alert、emergency(对应为:信息、提示、警告、错误、紧要、警戒、紧急)。看来,在安全方面的要求是与众不同的。
 
AIX下动态库的释放
写了一个使用动态库的程序,在程序退出后,所使用的动态库仍然不让覆盖,提示:“Cannot open or remove a file containing a running program ”。原以为是程序写的不对,后来在网上搜索,发现这是AIX上的一个特点,好象不是BUG。可以执行命令slibclean将不用的动态库卸载掉。
 
 
奇怪的代码
如下代码,在VC6中,若声明m3,则程序宕掉,去掉m3的声明,程序运行正常。为什么??:-(

#include <stdio.h>
#include <string>
#include <map>
using namespace std;
 
class A
{
public:
 void f1(){
  printf("aaaaaa/n");
 }
};
 
int main()
{
 map<string, A*> *m1, m3;
 map<string, void*> m2;
 
 A* pa = new A;
 m2.insert(make_pair(string("a"), (void*)pa));
 m1 = (map<string, A*>*)(&m2);
 A* pa2 = m1->find("a")->second;
 pa2->f1();
 
 return 0;
}

函数参数的压栈顺序
在将AutumnFramework向linux上移植时,用ubuntu6.06,上面是gcc 4.0.3。发现其参数传递时压栈顺序好象是从左向右的,跟VC6中不一样!不应该呀!
在BeanWrapperMacro.h中,我将一个数组的各个元素作为参数传递给bean的构造方法。在VC6中,我采用如下方式作为构造方法的参数传入:
        pPrams[--num]
其中,num是数组的元素个数,这样,按照从右向左的压栈顺序,数组的最后一个元素正好作为构造方法的最后一个参数。但在gcc 4.0.3中却不行,我改为:
        pPrams[i++]
i从0开始,程序正常了。这样的话,只能说明压栈顺序是从左向右,数组的第一个元素正好作为方法的第一个参数,否则程序就不应该正常。
但是,单独做一个小程序做实验,却发现不是这样。gcc 4.0.3的压栈顺序是从右向左的,VC6却出现了莫名其妙的结果,好象是压栈之前做了处理。以下是实验程序:

#include <stdio.h>
class A
{
public:
 A(int i, int j, int k){
  printf("k=%d:[%x]/n",k,&k);
  printf("j=%d:[%x]/n",j,&j);
  printf("i=%d:[%x]/n",i,&i);
 }
};
 
int f(int i,int j,int k);
int main()
{
    static int i=0;
    f(i++,i++,i++);
    A af(i++,i++,i++);
 return 0;
}
 
int f(int i,int j,int k)
{
 int l;
 int g;
 printf("k=%d:[%x]/n",k,&k);
 printf("j=%d:[%x]/n",j,&j);
 printf("i=%d:[%x]/n",i,&i);
 printf("___________/n");
 printf("l:%x/n",&l);
 printf("g:%x/n",&g);
 return 0;
}

这段程序大部分是从网上拷贝的,网上说,如果参数k->i的地址的增长顺序如果和局部变量l->g相同的话,就是从右到左,否则就是从左到右。按照此说法,gcc 4.0.3是从右到左,VC6倒是从左向右!!
乱了!!
 
既开始,则坚持
今天又看了一下autumn framework的下载情况,有点儿小孩的心理吧,总希望自己的东西能引起别人的注意。好象近几天每天都增加一个下载,心里挺得意:)。便有意在Google上搜索了一下autumn framework,翻了半天,终于发现,除了code.google.com外,还有一个地方提到了autumn framework: http://en.wikipedia.org/wiki/Dependency_injection,上面列出了当前各种语言的DI框架,对于C++来说,只有两个:QtIocContainer和Autumn Framework,前一个是john.zhihong.wang的。
看来还是有人注意了哦,那就坚持下去吧!
 
 
AutumnFramework 0.1.0 beta for Windows VC6 released

This project implements Dependency Injection for C++. It supports property setter injection and constructor injection. First it is compiled with VC6 and will be compiled on multiple platform. It supports POCO (Plain Old C++ Object) and has no constraints on the classes you create. It may has half-incursion to your program because you should define bean's wrapper using some macros.

2007-01-16 Ver 0.1.0 beta for Windows VC6 released. You can download it here.

It may be very babyish now, because I don't know Spring well and do this to imitate Spring in a hurry. I will write some doc ASAP. Now, it support:

  1. Support property injection and constructor injection.Only two constructor now, one has arguments and one has no argument. The setter function name should be like setXXX where XXX is a property name. You may not obey that rule if you rewrite the file BeanWrapperMacro.h.
  2. Support following basic type: char, short, int, long, float, double, char*, string, and some docrated with unsigned.
  3. Support class injection. (A class is named as a bean in Autumn like in Spring.)
  4. Support customized type using interface IBasicType.
  5. Support pointer to above types.
  6. Support singleton.
  7. Support initialization and destroyation functions.
  8. Support property setter overloading.
  9. Supoort dependence(it may be not like Spring).
  10. Support multiple dynamic libraries and local library(bean is in main process).
  11. Support configuration of log file path and level.
奇怪的String类型
今天做AutumnFramework的测试,发现了一个奇怪的现象,经验证是这样的,但还不清楚为什么。(环境VC)
把string类型作为属性值注入或采用构造子注入,在debug模式下都没有问题。当用Release模式时,作为属性值注入,仍然没有问题。但作为构造子注入,就出现错误了,导致类的其它属性的值也不正确了,真是奇怪。
在这个测试类中,把所有的基本类型都采用构造子注入,如果不注入string类型,大家都很正常;如果注入string类型,则只有每一个属性正常,其它属性的值都出错了。
原来记得好象在跟动态库传递string类型的参数好象有问题。但采用属性注入时又没有发现问题,在构造子注入发现问题,很是奇怪。
又发现,当把它单独作为参数进行构造子注入时,它自己是正常的。
 
 
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值