一个C#的泛型工厂

遇到类似以下情况:

class Base
{
    public enum TestType
    {
        TYPE_A,
        TYPE_B
    }
}

class TypeA:Base
{

}

class TypeB:Base
{

}

根据枚举类型不同而创建不同子类的时候,我们需要一个工厂来进行管理。
在泛型工厂中,需要保存构造函数指针和对应的枚举类型映射,于是需要有添加创建器的操作。
一个很方便使用的泛型工厂代码如下:

Read more

函数封装的改进(C++ 11不定长模板的加入)

在上文中,介绍了一种使用C++ 11动态类型推导封装函数的方法,但是依然没有摆脱函数不定参数封装的传统模式。今天借助C++ 11的不定长模板参数功能,来解决掉这个问题.

首先回顾一下要达到的要求:

  • 可以支持封装静态函数(全局函数)和类成员函数
  • 被封装的函数可以接受任意数量和类型的参数
  • 调用者可以拿到直接的真正的被封装函数的返回值和返回类型
  • 封装后的成员函数,可以在真正运行的时候才接受绑定的this object

这篇文章要解决的就是第二点.
封装依然得分为静态(全局)函数和类成员函数分别封装.

静态(全局)函数

代码:

template<class Func, class ... Args>
class Callable
{
private:
    Func functor_;

public:
    Callable(Func functor)
        : functor_(functor)
    {

    }

    auto run(Args&&... args)    ->decltype(functor_(args...))
    {
        return functor_(args...);
    }
};

template <class Ret, class ...Args>
auto MakeCallable(Ret(*func)(Args...)) ->decltype(new Callable<Ret(*)(Args...), Args...>(func))
{
    return new Callable<Ret(*)(Args...),Args...>(func);
}

和前文不同的地方在于,多了一个…Args模板参数,这个表示不定长模板参数,使用…来标记.这样就能做到匹配到任意参数个数的函数,不再需要像前文那样根据参数个数不同写多个这样的包装函数实现.

Read more

一种函数Callback的封装(使用到C++11)

本文想要实现的函数封装功能有如下:

  1. 可以支持封装静态函数(全局函数)和类成员函数
  2. 被封装的函数可以接受任意数量和类型的参数
  3. 调用者可以拿到直接的真正的被封装函数的返回值和返回类型
  4. 封装后的成员函数,可以在真正运行的时候才接受绑定的this object

原理

无论是__cdecl(缺省)还是__thiscall函数,我们都可以通过函数指针存储起来,借助C++的模板,于是我们可以存储任意类型的函数指针。同时借助C++11的返回值类型推导,于是我们可以让使用者拿到真正的函数返回值. 如果你不熟悉C++11的返回值类型推导内容,请点击这里.

实现

静态函数(全局函数)

先直接看代码:

template <class Func> 
class Callback0 
{
public:
    typedef decltype(Func()) result_type;

    Callback0(Func functor)
        : functor_(functor)
    { }

    auto run() ->decltype(Func())
    { 
        return functor_();
    }
private:
    Func functor_;
};

template <class Func,class A0>
class Callback1
{
public:
    typedef decltype(Func()(a0)) result_type;

    Callback1(Func functor)
        : functor_(functor)
    {

    }

    auto run(A0 a0) ->decltype(Func()(a0))
    {
        return functor_(a0);
    }

private: 

    Func functor_;
};

template <class Func, class A0, class A1>
class Callback2
{
public:
    typedef decltype(Func()(a0,a1)) result_type;

    Callback2(Func functor)
        : functor_(functor)
    {

    }

    auto run(A0 a0, A1 a1) ->decltype(Func()(a0, a1))
    {
        return functor_(a0,a1);
    }

private:

    Func functor_;
};

template <class Ret>
auto MakeCallback(Ret(*func)()) ->decltype(new Callback0<Ret(*)()>(func))
{
    return new Callback0<Ret(*)()>(func);
}

template <class Ret, class A0>
auto MakeCallback(Ret(*func)(A0)) ->decltype(new Callback1<Ret(*)(A0), A0>(func))
{
    return new Callback1<Ret(*)(A0), A0>(func);
}

template <class Ret, class A0, class A1>
auto MakeCallback(Ret(*func)(A0, A1))   ->decltype(new Callback2<Ret(*)(A0,A1), A0,A1>(func))
{
    return new Callback2<Ret(*)(A0,A1), A0, A1>(func);
}

这里实现了一个最多支援两个参数的静态函数封装,但是支援无限多个参数,也只需要再写多个类似的结构即可。这个部分其实是和早前的C++规范是类似的。

Read more

Cocos2d-x 程序移植到Windows Phone 8小记

将一个在iOS和Android上运行良好的Cocos2d-x 程序移植到Windows Phone 8的设备。

网路上面有一些文章,大体可以按照这些文章来进行,下面说几点遇到的坑:

1.E_OUTOFMEMORY

程序移植过去后,在Lumia 920上运行的好好的,但是在更高配的Lumia 1520却报

D3D11 ERROR: ID3D11Device::CreateTexture2D: Returning E_OUTOFMEMORY, meaning memory was exhausted

这样的错误,实在匪夷所思。毕竟Lumia 1520的配置远比Lumia 920要好。

不管怎么样,参照:http://msdn.microsoft.com/zh-cn/library/windowsphone/develop/jj681682%28v=vs.105%29.aspx  这篇文章,添加了ID_FUNCCAP_EXTEND_MEM标记后运行OK了。

2.使用自己的shader

这实在耗费了我一些时间,另外开文在此:http://www.ispinel.com/2014/07/03/12393/

3.libcurl

这个工程在cocos2dx\platform\third_party\wp8\libcurl,添加进解决方案,然后把项目引用其就行。

4.多线程

由于之前的代码广泛使用到pthread,遇到这块的时候,我一方面参考了CCPThreadWinRT.h这个文件设定(后来也被废除),另外一方面使用了C++ 11的std::thread以及Parallel Patterns Library进行取代.

5.中文输入法

这个问题解决的我实在想说一句WTF。如果在UI库上,激活输入法是通过CCEGLView::setIMEKeyboardState方式,那么会遇到问题。在Windows Phone 8下面会出现在中文输入法时,会同时接收到用户输入的英文和中文,而在Windows Phone 8.1下面,则完全不能接受到中文。即便Cocos2d-x 的官方sample也是如此。解决方式:项目改成XAML模式,当要激活键盘输入时,通过CCEGLView::OpenXamlEditBox来打开一个单独的专门输入页面。

Read more

Cocos2d-x:在Windows Phone/Windows RT上使用自定义Shader

Cocos2d-x 是一个新兴的并且非常流行的2D开源游戏引擎,现在主要面向移动平台,其在2.2版本开始终于支持Windows Phone 8 和 Windows RT平台.

通常来说,在成品游戏上,都需要首先将shader预编译,然后再给游戏所用,因为这能减少游戏中的性能消耗。

但是估计Cocos2d-x的游戏对shader要求不高,预设shader也比较简单和少,导致其并没有这样一套shader预编译的机制,一直都是游戏直接在运行时编译shader.

于是到了Windows Phone/Windows RT这样的平台,其强制要求使用预编译shader,而不能在游戏运行时编译shader,因此Cocos2d-x在解决这个问题的时候,就犯难了.

另外Windows Phone/Windows RT仅仅支持DirectX(feature level 是9.3),而Cocos2d-x从设计之初就从来未想过要对渲染器进行包装(似乎假设了所有硬件都能运行OpenGL一样),导致OpenGL代码在整个引擎中满天飞,这是我严重不喜欢Cocos2d-x的一个地方.

于是遇到Windows Phone/Windows RT这样的仅仅支持DirectX的平台,Cocos2d-x想要支持,便变得非常的困难,需要重新设计整个渲染器才行。

幸亏,有了Google.

AngelProject是一个来自Google的开源项目,可以将OpenGL ES的接口转换成DirectX 9/11。于是Cocos2d-x使用了AngelProject,终于能支持Windows Phone和Windows RT.

在这里使用的版本是Cocos2d-x 2.2.3.

在加载shader的时候,通过代码查看可知,在CCGLProgram::initWithVertexShaderByteArray函数,如果是Windows Phone/Windows RT平台,会跳转到initWithPrecompiledProgramByteArray函数,进一步到CCPrecompiledShaders::loadProgram加载预编译shader二进制代码.

CCPrecompiledShaders类有一个computeHash函数,用来计算你输入shader代码对应的SHA1码,以此为Key,再到precompiledshaders.h中查找对应的shaer预编译二进制码.

换句话说,如果你不小心修改了ccShaders.cpp中引用的那些shader文本文件,即使你仅仅只是添加了一个空格,或者添加了一个空行等等看起来毫无影响的事情,但是实际上其生产的SHA1码已经改变,于是导致无法在预编译shader中找到对应的二进制代码.

非常愚蠢的设定.

于是接下来是个问题:我有一个shader,是自己写的,需要给Cocos2d-x使用,该如何做呢?

以下步骤:

Read more

C++11:右值引用-移动语义

先来看这样一个例子:

class TestClass
{
public:
	TestClass()
	{
		d = new int[10];
	}
	TestClass(const TestClass &t)
	{
		d = new int[10];
	}
	~TestClass()
	{
		delete d;
		d = nullptr;
	}

private:
	int *d;
};

TestClass get_test_class()
{
	TestClass t;
	return t;
}

void run_test()
{
	TestClass t2 = get_test_class();
}

在这个例子中,首先get_test_class函数中的TestClass t语句进行了一次构造,并且开辟了heap内存,接着在值传回来时,其拷贝构造函数为了实现深拷贝,又开辟了一次heap内存。也就是说,这样为了拿到一个对象,总共有两次heap内存的开辟。并且其中一次是临时变量,其生命期结束后就会被销毁,看起来似乎造成了性能的浪费。

Read more

return top