资料收集[置顶]
在这边放一些有用的资料索引,方便自用,慢慢收集整理了
git入门推荐,中文的哦
http://progit.org/book/zh/
twitter的scala教程
http://twitter.github.com/scala_school/index.html
stackoverflow的scala tag
http://stackoverflow.com/tags/scala/info
markdown语法
http://daringfireball.net/projects/markdown/syntax
关于模板的一些有趣的东西(续)
模板类不会展开未引用的函数
一般情况下,下面的代码是不能通过编译的:
int FooFunc();
class FooClass
{
public:
int FuncA() { return FooFunc(); }
int FuncB() { return FooFunc(0); }
};
原因很简单,因为在FooClass::FuncB里面使用了未重载过的带一个参数的函数FooFunc。
但是如果换下面的形式:
template<typename>
class FooClass
{
public:
int FuncA() { return FooFunc(); }
int FuncB() { return FooFunc(0); }
};
void TestFunc()
{
FooClass<void/*Could be any template parameter*/> obj;
obj.FuncA();
}
这样的FooClass类是可以被编译,并且FooClass::FuncA是可以被正确调用的,哪怕他似乎包含了一个看上去就知道是错误的函数FooClass::FuncB。
但是如果换成下面的形式:
void TestFunc()
{
FooClass<void/*Could be any template parameter*/> obj;
obj.FuncB();
}
显式调用FooClass::FuncB是会触发编译错误的。因此我们似乎可以得出一种结论:模板类不会展开未引用的函数。
关于用例,我目前只是在实现能支持任意参数数量的模板函数器类里面用到过,暂时还想不到会被广泛使用的例子。
虚跟模板可以兼得?!(续)
模板不可用
有种项目开发模式叫做模块式开发,简而言之就是将项目功能分隔成一个个独立的模块,然后每个模块提供预先商定好的接口以便各模块之间的通信。往往在这种模式下面,接口都是通过纯虚的方法定义的,所以在这种开发模式下面,模板是不可用的。
(伪)灵活调用接口
模板提供的方便之处之一便在于调用时可以了传递任意类型的数据而不需要分别针对性的实现。但是在模块接口式开发的模式下面,自然不能直接定义模板化接口。目前来说,我能想到的就是手动重载函数的针对各种可接受类型的版本,然后同样他们都需要被虚化。例如:
class FooInterface
{
virtual void FooFunc(const char& value) = 0;
virtual void FooFunc(const int& value) = 0;
virtual void FooFunc(const long& value) = 0;
virtual void FooFunc(const float& value) = 0;
}
(伪)结合模板与虚函数
其实这也不是一种很优雅的解决方式,简单来说就是通过模板将任意类型都映射到那些手动定义的虚方法上面。例如:
class FooInterface
{
template<typename ValueType>
void FooFunc(const ValueType& value)
{
FooFunc(ToCompatibleValueType(value));
}
virtual void FooFunc(const char& value) = 0;
virtual void FooFunc(const int& value) = 0;
virtual void FooFunc(const long& value) = 0;
virtual void FooFunc(const float& value) = 0;
}
这样一来你就可以将任意类型的参数传递给这个FooFunc而无需修改添加多余的虚方法的声明定义。但是前提是这里面的ToCompatibleValueType函数能够将你提供的这些任意类型的参数给映射成那些已经被手动重载的版本。
意义何在?
当手动重载的FooFunc函数版本有限,而调用的地方又很多,原始参数类型也多种多样的时候,因为FooFunc这个接口本身是虚的不能模板化,我们不得不面对大量各种类型转换的调用。而通过这个多出来的模板化的FooFunc,我们可以在ToCompatibleValueType里面对所有类型做集中的类型转换(当然这里会用到很多的Meta Programming以及模板偏特化等等)。而在我们调用该方法的地方则可以传递任意类型数据给该函数,看上去便会有类似模板函数那样方便的效果。
ToCompatibleValueType就像是一个过滤器一样,尝试将任意类型转换为可兼容那些手动指定的数据类型,当然如果发现有些类型无法转换的时候,触发一个编译期的错误提醒开发人员也是很有作用的。
关于模板的一些有趣的东西
来自boost,Loki等等,大多数代码需要手动或者自动重复(参考boost pre-processor repetition)若干份来满足不同需要。
让模板“支持”任意数量类型参数
将多种类型融合为一种类型,用于让模板可以接收任意多类型作为参数:
定义一种“空类型”以及“类型链”
struct NullType {};
template<typename _H, typename _T>
struct TypeChain
{
typedef _H Head;
typedef _T Tail;
};
然后就可以有:
TypeChain<_T1, NullType> TypeChain<_T1, TypeChain<_T2, NullType>> TypeChain<_T1, TypeChain<_T2, TypeChain<_T3, NullType>>>
用这些类型都可以带入其他模板用作类型参数。更多参数版本可以通过repetition自动展开。
解析函数原型中返回值以及所有参数类型
通过模板偏特化实现对函数原型,首先定义最原始的解析模板类:
template<typename> struct Signature;
然后分别偏特化无参数,有一个参数,有二个参数到有若干个参数的版本例如:
template<typename _R>
struct Signature<_R (*)()>
{
typedef _R ResultType;
};
template<typename _R, typename _P1>
struct Signature<_R (*)(_P1)>
{
typedef _R ResultType;
typedef _P1 ParamType1;
};
template<typename _R, typename _P1, typename _P2>
struct Signature<_R (*)(_P1, _P2)>
{
typedef _R ResultType;
typedef _P1 ParamType1;
typedef _P2 ParamType2;
};
该模板可以直接接受函数原型作为模板参数,例如:
Signature<int(float, char)>
然后可以进一步获取返回值和参数的类型,例如:
typedef Signature<int(float, char)> TheSignature; typedef typename TheSignature::ResultType ResultType; // Type int typedef typename TheSignature::ParamType1 ParamType1; // Type float typedef typename TheSignature::ParamType2 ParamType2; // Type char
更多参数版本依然可以通过repetition来自动实现。
虚跟模板可以兼得?!
由某个案例引发出来...
为什么要虚
在OOP的开发过程中,往往会有大量对象的定义和使用,很多时候我们需要将这些对象持久化的保持在外部文件里面,然后再在需要的时候将其读取进来。因为对象的类型数量都很多,一个一个去实现如何写入和读取就特别冗余且笨重。常见的解决方式便是序(反序)列化,简而言之就是为对象的基类都加上serialize/deserialize的虚方法,然后子类只需要实现如何组织需要写入和读取的数据就可以了。此后我们只要有一个serializable的对象便能对其进行读写了。
为什么要模板
但是关于具体的序列化的时候,便会遇到一些跟平台相关的问题,比如不能确定这个对象最后写入到的是一个本地文件、网络流还是JSON这样的中间载体的时候该怎么办?因此我们提供给序列化接口的序列化器这时变得不那么确定了。此外我们也不希望对象的序列化接口明确的知道序列化器是什么,因为这样会让其产生依赖使得对象的使用不独立。综上,我们希望一种模板化的序列化器,只有到对象用到它的时候才会被具体的展开使用。
虚跟模板可以兼得?!
所以我们需要给对象的基类定义一个方法看上去会是:
template<typename Serializer> virual void Serialize(Serializer& serializer);
但是遗憾的告诉你这是不可能的。为什么?
首先抛开C++的标准不说,单从编译器的实现上面就能否定这一点。虚函数具体是通过虚函数表来表现其多态性的,这个虚函数表实际上是一个函数地址的数组,在建立这个虚函数表的时候所有的虚函数都需要被实现了,这样才能找到有效的函数地址。但另一方面模板方法本身是并不存在的方法,只有在被用到的时候才被具体展开并实例化。因此模板方法的“不确定”性与虚函数的“必须确定”性在理论上便已经矛盾冲突了。
解决方案
首先,为了模板化,不得不先将virtual去掉,这样要求所有对象里面的序列号方法被重新声明为:
template<typename Serializer> void Serialize(Serializer& serializer);
但是作为序列化方法的调用者,它所能拿到的是所有对象的基类的指针或者引用,它也不能直接调用到子类里面的具体方法。为此目前我能想出的办法就是向下转型,例如:
void Serialize(Base& base)
{
FooSeraizlier fooSerializer;
switch(base.GetClassId())
{
case DeviedA::ClassID:
(static_cast<DeviedA*>(&base))->Serialize(fooSerializer);
break;
case DeviedB::ClassID:
(static_cast<DeviedB*>(&base))->Serialize(fooSerializer);
break;
case DeviedC::ClassID:
(static_cast<DeviedC*>(&base))->Serialize(fooSerializer);
break;
......
}
}
无奈的是类似这样switch case的向下转型必须要手写的,因此当类型过多的时候肯定是很悲剧的,但是如果你有类似python一类的脚本工具来帮你自动生成代码的话,那就放心的使用吧。
关于脚本
一些关于脚本的琐碎的思绪。
如果说Programming Language是对应基础框架以及底层兼容的偏硬性问题的解决方案,那Script Language便是某种意义上真正面向应用功能的解决方案。Programming Language的初衷是调用平台的各种资源与功能,为了达到最好的效果往往都是贴近底层开发的,在这方面“硬”的东西比较多,而且一旦敲定,便不会容易修改。简而言之,Hardcode大概就是这样一种意思。
太硬的东西无论是开发者还是客户都所不愿意见到的,他们不希望自己买了一辆自行车,轮子只能转到100圈,除非他们都是2B。
于是乎有一种叫Data Driven的概念出现了。大约来讲它属于已经被Hardcode编译成计算机本地指令的程序的外部配置信息。程序的代码已经实现了开发过程中能够预见的大多数功能,这些功能可以接收一定的可变的参数来达到一定程度的“动态性”。这基本上是目前绝大多数项目的一种比较统一的解决方案,无论是application,service或者game。
这种开发模式有一个比较重要的平衡点需要掌握:我们需要将多少功能给做出来,又需要将多少功能可以进行外部的配置。前者过多就会导致过多的硬编码,而后者过多就会导致过于零散的功能以及过于复杂的配置。
无论如何,硬编码是我们是在不想看到的,但是如果功能做得过于原子化,那外部的配置必然会相当复杂。要知道在一个团队里面,负责这部分数据生产的不一定都是城续猿。
有时候我们不能太明白的说明,我们到底想要一个什么样具体的功能,城续猿没办法做出一个抽象的模棱两可的功能然后再暴露一些有限的参数供团队里面其他成员使用,而后者往往也说不清楚自己到底想要什么样的东西。但就算双方能达成协议,通过修改代码来实现新功能以及调试的代价往往也是比较昂贵的。
那么,为何不让他们自己去做?
于是我们需要一种,简单,可用,且能比较高效开发的方式 —— 脚本。
理论上来讲脚本也是一种编码语言,它同样有分支,有循环,有变量,只不过其所在的层次不一样。脚本其实也是Data Driven里面的数据,只不过与单纯的贴图,动画,音效等纯数据相比它更有一种自我描述性。换言之它们能自己说明自己的目的,而不是简单的被贴到某个设置好了UV的模型上面去。
城续猿可以做好一系列的相对独立的功能,这些功能之间可以拼凑组合,相互调用,往往我们会将它们称之为API,最终它们会暴露给脚本使用。
在此基础上,如果说通过Programming Language实现的框架模型是“第一次开发”,那么通过Script Language我们可以在项目中进行“第二次开发”。这也就是一种理想的“准备阶段”往“生产阶段”过渡的方式。案例例如游戏中各种角色的AI,任务,界面UI等等。
待续…
Scala和Java的互操作
最近在实践中遇到一些Scala和Java互操作的问题,记录一下
BTW: gist + wordpress的gist插件各种好使啊 果断抛弃各种syntax highlighter插件
Scala Map Java Map
下面代码是两个隐式转换,用于在两者之间互转
Scala repeated parameters Java varargs
不好好看书就急着动手的杯具……纠结了很久才知道如何正确调用可变参数列表的Java方法
如果不使用: _*这个annotation告诉scala我要展开成varargs,不论你如果toArray如果反射new出Object数组都是没有用的,整个Array或者List会被作为一个Object传入,而不是展开成多个参数传入调用方法
来scala下Scala
Scala,所谓可伸缩可扩展,于是就蛋疼试试
首先因为scala的语句块都可以返回值,因此if else完全可以取代?:三元运算符的作用,不过写起来还是后者比较爽。那就自己扩展一把好了。
class BooleanEx(b: Boolean) {
def ?[T](func: Boolean => T) = {
func(b)
}
}
class AnyEx[T](o: T) {
def or(o1: T) = { b: Boolean =>
if (b) {
o
} else {
o1
}
}
}
object Helper {
implicit def Boolean2BooleanEx(b: Boolean) = new BooleanEx(b)
implicit def Any2AnyEx[T](o: T) = new AnyEx(o)
def main(args: Array[String]) {
(1 == 1) ? (true or false)
}
}
无奈的地方是没法使用冒号作为方法名(因为是关键字),以及?属于最高优先级层,所以后面的括号省略不能……