[英]Automating explicit template instantiation
To reduce compile times in a template-heavy project, I'm trying to explicitly instantiate many templates in a separate compilation unit.为了减少模板繁重的项目中的编译时间,我试图在单独的编译单元中显式实例化许多模板。 Because these templates depend on
enum class
members, I'm able to list all possible instantiations.因为这些模板依赖于
enum class
成员,所以我能够列出所有可能的实例化。 I want all other cpp-files to only see the declaration.我希望所有其他 cpp 文件只看到声明。 While I'm able to do this, I run into problems trying to factorize the explicit instantiations.
虽然我能够做到这一点,但我在尝试分解显式实例时遇到了问题。 I will first explain 2 working examples below, in order to explain what exactly my issue is (example 3):
我将首先解释下面的 2 个工作示例,以解释我的问题到底是什么(示例 3):
Example 1示例 1
/* test.h
Contains the function-template-declaration, not the implementation.
*/
enum class Enum
{
Member1,
Member2,
Member3
};
struct Type
{
template <Enum Value>
int get() const;
};
/* test.cpp
Only the declaration is visible -> needs to link against correct instantiation.
*/
#include "test.h"
int main() {
std::cout << Type{}.get<Enum::Member1>() << '\n';
}
/* test.tpp
.tpp extension indicates that it contains template implementations.
*/
#include "test.h"
template <Enum Value>
int Type::get() const
{
return static_cast<int>(Value); // silly implementation
}
/* instantiate.cpp
Explicitly instantiate for each of the enum members.
*/
#include "test.tpp"
template int Type::get<Enum::Member1>() const;
template int Type::get<Enum::Member2>() const;
template int Type::get<Enum::Member3>() const;
As mentioned before, the above compiles and links without issues.如前所述,上面的编译和链接没有问题。 However, in the real application, I have many function-templates and many more enum-members.
然而,在实际应用中,我有很多函数模板和更多枚举成员。 Therefore, I tried making my life somewhat easier by grouping the members together in a new class, which itself depends on the template parameter and explicitly instantiate this class for each of the enum-values.
因此,我尝试通过将成员分组到一个新的 class 中来让我的生活更轻松,它本身取决于模板参数并为每个枚举值显式实例化此 class。
Example 2示例 2
// instantiate.cpp
#include "test.tpp"
template <Enum Value>
struct Instantiate
{
using Function = int (Type::*)() const;
static constexpr Function f1 = Type::get<Value>;
// many more member-functions
};
template class Instantiate<Enum::Member1>;
template class Instantiate<Enum::Member2>;
template class Instantiate<Enum::Member3>;
This still works (because in order to initialize a pointer to a member, this member has to be instantiated), but when the number of enum-members is large, it will still be messy.这仍然有效(因为为了初始化指向成员的指针,必须实例化该成员),但是当枚举成员的数量很大时,它仍然会很乱。 Now I can finally get to the issue.
现在我终于可以解决这个问题了。 I thought I could factorize even further by defining a new class-template that depends on a parameter pack, which then derives from each of the types in the pack like so:
我想我可以通过定义一个依赖于参数包的新类模板来进一步分解,然后它从包中的每个类型派生,如下所示:
Example 3示例 3
// instantiate.cpp
#include "test.tpp"
template <Enum Value>
struct Instantiate { /* same as before */ };
template <Enum ... Pack>
struct InstantiateAll:
Instantiate<Pack> ...
{};
template class InstantiateAll<Enum::Member1, Enum::Member2, Enum::Member3>;
This should work, right?这应该有效,对吧? In order to instantiate
InstantiateAll<...>
, each of the derived classes have to be instantiated.为了实例
InstantiateAll<...>
,必须实例化每个派生类。 At least, this is what I thought.至少,这是我的想法。 The above compiles but results in a linker-error.
以上编译但会导致链接器错误。 Upon checking the symbol-table of
instantiate.o
with nm
, it's confirmed that nothing at all has been instantiated.用
nm
检查instantiate.o
的符号表后,确认没有任何东西被实例化。 Why not?为什么不?
Of course, I can get by using example 2, but it really got me curious why things break down like this.当然,我可以通过使用示例 2 得到,但这真的让我很好奇为什么事情会这样崩溃。
(Compiling with GCC 10.2.0) (使用 GCC 10.2.0 编译)
Edit: same happens on Clang 8.0.1 (although I have to use the address-of-operator explicitly in assigning the function-pointers: Function f1 = &Type::get<Value>;
)编辑:在 Clang 8.0.1 上也会发生同样的情况(尽管我必须在分配函数指针时明确使用运算符地址:
Function f1 = &Type::get<Value>;
)
Edit: User 2b-t kindly made the examples available through https://www.onlinegdb.com/HyGr7w0fv_ for people to experiment with.编辑:用户 2b-t 请通过https://www.onlinegdb.com/HyGr7w0fv_提供示例供人们试验。
If the compiler sees that code isn't referred to, even for static initialization with side effects, it can eliminate it, and I think that's the case in your example.如果编译器发现没有引用该代码,即使对于具有副作用的 static 初始化,它也可以消除它,我认为您的示例就是这种情况。 It can "prove" those class instantiations are not used, and so the side effects are lost.
它可以“证明”那些 class 实例没有被使用,因此副作用就消失了。
For a non-standard solution, but one that works on g++ (and presumably clang, but not tested) is to mark your static data members with the "used" attribute:对于非标准解决方案,但适用于 g++(可能是 clang,但未经测试)的解决方案是用“used”属性标记您的 static 数据成员:
template <Enum Value>
struct Instantiate
{
using Function = int (Type::*)() const;
static constexpr Function f1 __attribute__((used)) = &Type::get<Value>;
// many more member-functions
};
Update更新
Reviewing the standard, the wording seems like I got it exactly backwards:回顾标准,措辞似乎完全倒退了:
"If an object of static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy may be eliminated as specified in..."
"If an object of static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused, except that a class object or its copy may be eliminated as specified in..."
So I've had this in my head for decades, and now I'm uncertain as to what I was thinking.所以几十年来我一直在想这个,现在我不确定我在想什么。 :) But it seems related, given the attribute helps.
:) 但它似乎相关,因为属性有帮助。 But now I have to learn what's going on.
但现在我必须了解发生了什么。
I can't give you yet a good answer to why this does not work (maybe I can do so later or somebody else can) but instead of having Instantiate
and InstantiateAll
having only a variadic InstantiateAll
as follows works我还不能给你一个很好的答案来解释为什么这不起作用(也许我以后可以这样做,或者其他人可以这样做),而是让
Instantiate
和InstantiateAll
只有一个可变参数InstantiateAll
,如下所示
template <Enum ... Pack>
struct InstantiateAll {
using Function = int (Type::*)() const;
static constexpr std::array<Function,sizeof...(Pack)> f = {&Type::get<Pack> ...};
};
template class InstantiateAll<Enum::Member1, Enum::Member2, Enum::Member3>;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.