在我看来,函数指针是最好的方法。
这种方法之前已经提到过,但是我想更深入地讨论一下对箭头类型的代码使用这种方法的优点。
根据我的经验,这种if链发生在程序的某个操作的初始化部分。程序在尝试启动之前需要确保一切正常。
通常情况下,在许多do stuff函数中一些东西可能会被分配,或者所有权可能会改变。如果你失败了,你会想要反转这个过程。
假设你有以下3个函数:
bool loadResources()
{
return attemptToLoadResources();
}
bool getGlobalMutex()
{
return attemptToGetGlobalMutex();
}
bool startInfernalMachine()
{
return attemptToStartInfernalMachine();
}
所有函数的原型将是:
typdef bool (*initializerFunc)(void);
如上所述,您将使用push_back将指针添加到一个向量中,并按顺序运行它们。但是,如果您的程序在startInfernalMachine上失败,您将需要手动返回互斥量并卸载资源。如果在RunAllways函数中执行此操作,则会遇到麻烦。
但是等等!函子是非常棒的(有时),你可以只改变原型如下:
typdef bool (*initializerFunc)(bool);
为什么?好的,新函数现在看起来像这样:
bool loadResources(bool bLoad)
{
if (bLoad)
return attemptToLoadResources();
else
return attemptToUnloadResources();
}
bool getGlobalMutex(bool bGet)
{
if (bGet)
return attemptToGetGlobalMutex();
else
return releaseGlobalMutex();
}
...
所以现在,整个代码看起来就像这样:
vector<initializerFunc> funcs;
funcs.push_back(&loadResources);
funcs.push_back(&getGlobalMutex);
funcs.push_back(&startInfernalMachine);
// yeah, i know, i don't use iterators
int lastIdx;
for (int i=0;i<funcs.size();i++)
{
if (funcs[i](true))
lastIdx=i;
else
break;
}
// time to check if everything is peachy
if (lastIdx!=funcs.size()-1)
{
// sad face, undo
for (int i=lastIdx;i>=0;i++)
funcs[i](false);
}
因此,自动清理项目绝对是向前迈出的一步,并通过这个阶段。
然而,实现有点尴尬,因为您需要反复使用这个推回机制。如果你只有一个这样的位置,我们说它是可以的,但如果你有10个位置,有一个振荡的函数数量……这可不好玩。
幸运的是,还有另一种机制可以让您实现更好的抽象:可变函数。
毕竟,有许多不同数量的函数需要仔细研究。
变进函数是这样的:
bool variadicInitialization(int nFuncs,...)
{
bool rez;
int lastIdx;
initializerFunccur;
vector<initializerFunc> reverse;
va_list vl;
va_start(vl,nFuncs);
for (int i=0;i<nFuncs;i++)
{
cur = va_arg(vl,initializerFunc);
reverse.push_back(cur);
rez= cur(true);
if (rez)
lastIdx=i;
if (!rez)
break;
}
va_end(vl);
if (!rez)
{
for (int i=lastIdx;i>=0;i--)
{
reverse[i](false);
}
}
return rez;
}
现在你的代码将被缩减(在应用程序的任何地方)为:
bool success = variadicInitialization(&loadResources,&getGlobalMutex,&startInfernalMachine);
doSomethingAllways();
通过这种方式,你可以用一个函数调用来完成所有那些讨厌的if列表,并确保当函数退出时,你不会有任何初始化的残留物。
您的团队成员将非常感激您在1行代码中实现了100行代码。
但是等等!
还有更多!
箭头类型代码的主要特征之一是需要有特定的顺序!
并且这个特定的顺序在整个应用程序中需要是相同的(多线程死锁避免规则1:在整个应用程序中始终以相同的顺序接受互斥)
如果有一个新来者,把函数按随机顺序排列呢?更糟糕的是,如果要求您将其公开给java或c#,该怎么办?(是的,跨平台是一种痛苦)
幸运的是,有一种方法可以解决这个问题。
以下是我的建议:
创建一个枚举,从第一个资源开始到最后一个资源
定义一个pair,从枚举中获取一个值,并将其与函数指针配对
把这些对放在一个向量中(我知道,我只是定义了一个映射的使用:),但我总是用向量表示小的数字)
将可变宏从函数指针改为整数(这很容易在java或c#中公开;)))
在变进函数中,对这些整数进行排序
运行时,运行分配给该整数的函数。
最后,您的代码将确保以下内容:
只要一行代码就可以初始化,不管需要多少东西
强制执行调用顺序:你不能在loadResources之前调用startInfernalMachine,除非你(架构师)决定允许这样做
如果在此过程中某些事情失败,则完成清理(考虑到您正确地进行了反初始化)
改变整个应用程序中初始化的顺序只意味着改变枚举中的顺序