我正在编写一个实体entity component system game engine。作为其中的一部分,我已经编写了一个Manager
类,它将注册各种IBase
实现,稍后,允许我示例化这些实现。请参阅下面的示例,了解我如何使用它。
class Manager{
public:
template<class T>
void registerDerived()
{ /*Register a Derived with the Manager*/ };
template<class T>
T createDerived()
{ /*if T is not registered, throw an error*/
return T();};
};
struct IBase{
};
struct Derived1 : public IBase{
};
struct Derived2 : public IBase{
};
字符串
正如注解中所指出的,我在template<class T>Manager::createDerived()
中有一段代码,它检查Base
的特定实现是否已经使用template<class T>Manager::registerDerived()
注册,如果它没有注册,它就会抛出一个错误。这个检查很简单,为了简单起见,代码示例中省略了它。
我的问题是:有没有可能把这个检查移到编译时,而不是等到运行时?看起来在运行时应该有足够的信息来做这个决定。
到目前为止,我已经探索/阅读了SFINAE,这似乎是要采取的方法,但我不知道如何在这种特定情况下使用这些习惯用法。This link很好地概述了基本SFINAE习惯用法,this SO问题给出了一些很好的代码片段,最后This blog post似乎几乎解决了我的确切情况。
下面是一个完整的例子,这是我试图实现这些链接中找到的信息:
#include <iostream>
class Manager{
public:
template<class T>
void registerDerived()
{ /*Register a Derived with the Manager*/ }
template<class T>
T createDerived()
{ /*if T is not registered, throw an error*/
return T();}
};
struct IBase{
};
struct Derived1 : public IBase{
};
struct Derived2 : public IBase{
};
template<typename T>
struct hasRegisterDerivedMethod{
template <class, class> class checker;
template <typename C>
static std::true_type test(checker<C, decltype(&Manager::template registerDerived<T>)> *);
template <typename C>
static std::false_type test(...);
typedef decltype(test<T>(nullptr)) type;
static const bool value = std::is_same<std::true_type, decltype(test<T>(nullptr))>::value;
};
int main(){
Manager myManager;
myManager.registerDerived<Derived1>();
// whoops, forgot to register Derived2!
Derived1 d1 = myManager.createDerived<Derived1>(); // compiles fine, runs fine. This is expected.
Derived2 d2 = myManager.createDerived<Derived2>(); // compiles fine, fails at runtime (due to check in createDerived)
std::cout << std::boolalpha;
// expect true, actual true
std::cout << "Derived1 check = " << hasRegisterDerivedMethod<Derived1>::value << std::endl;
// expect false, actual true
std::cout << "Derived2 check = " << hasRegisterDerivedMethod<Derived2>::value << std::endl;
return 0;
}
型
**
TL;DR
我如何修改上面的代码以产生编译时错误(可能使用static_assert
),而不是等到运行时才检测到错误?
**
2条答案
按热度按时间pu3pd22g1#
恕我直言,你有一个设计问题。
registerDerived<Derived>()
是调用createDerived<Derived>()
的先决条件这一事实应该用代码来表达(而不仅仅是在文档中),这样未注册的创建是不可能的。实现这一点的一种方法是通过注册文件,该文件在注册时发布,并在创建时要求。
字符串
注意,对象
t
可能被优化掉了。当然,这段代码与你的代码不同,因为它需要携带
ticket<Derived>
来进行任何创建。然而,在这个简单的例子中,注册后创建的概念并不合理,因为下面的代码总是可以工作,并且不需要预先注册(请参阅我在评论中的问题):型
如果每次查看的注册比我的简单示例中的过程更昂贵,那么您可以在尝试这样做之前检查(如上所述使用
unordered_set<type_index>
)是否注册了Derived
类型。hof1towb2#
我不认为这是可能的便携/可靠的方式。
如果您对仅在编译时注册感兴趣,我建议将
Manager
作为模板类,其中模板参数是注册的类型。我的意思是..
字符串
或(如Deduplicator所建议的)以更紧凑的方式
型
您可以使用它来启用/禁用SFINAE
createDerived()
,如下所示型
和
hasRegisterDerivedMethod
可以写成如下型
不幸的是,这在编译时有效,但在运行时无效,因此,如果您需要一个在编译时和运行时都有效的解决方案,那么此解决方案不适合您。
下面是一个完整的工作示例
型
主题:而不是
型
你可以写
型