简体   繁体   English

dllexport 类模板实例(专业化),减少仅标头模板库的编译时间

[英]dllexport class template instances (specializations), reducing compilation time for header-only template libraries

Is it possible to export some of the class template instances, while leaving to a library's user the ability of generating other specializations of a given class template (when compiling the executable).是否可以导出一些类模板实例,同时让库的用户能够生成给定类模板的其他特化(在编译可执行文件时)。

Given I have a public header鉴于我有一个公共标题

// public.h
#pragma once

#ifndef DLL_BUILD
#define API __declspec(dllimport)
#else 
#define API __declspec(dllexport)
#endif // !DLL_BUILD

#include <type_traits>


// dummy to generate .lib
struct API dummy
{
    void be_dummy();
};



template <class T>
struct Foo
{
    static T Sum(T a, T b)
    {
        static_assert(std::is_fundamental_v<T>);
        return a + b;
    }
};


With this way of declaring class template Foo every instantiation will happen inside the user's executable.通过这种声明类模板Foo的方式,每个实例化都将在用户的可执行文件中发生。

However, if I define Foo as dllexport/dllimport using API macro, every specialization of Foo which has not been explicitly instantiated inside the dll, will fail to link.但是,如果我使用API宏将Foo定义为dllexport/dllimport ,则没有在 dll 中明确实例化的Foo的每个特化都将无法链接。

// impl.cpp - dll

#include "public.h"


void dummy::be_dummy()
{
    volatile int a = 0;
    return;
}

template API struct Foo<int>;


///////////////////////////////////////////
// main.cpp - executable

#include "public.h"

#include <iostream>

int main()
{
    dummy().be_dummy();

    // std::cout << Foo<double>().Sum(4.12, 3.18) << std::endl; // Unresolved external symbol

    std::cout << Foo<int>().Sum(60, 9) << std::endl; // executed within the dll

    return 0;
}

So, is it possible to force the compiler to link against an existing class template instance when one has been exported, and to generate another that has not.因此,是否有可能强制编译器在一个已导出的情况下链接到现有的类模板实例,并生成另一个尚未导出的类模板实例。

UPDATE更新

I found a solution, see my answer below.我找到了解决方案,请参阅下面的答案。 I leave the old update just in case anybody will find such usage of SFINAE helpful.我保留旧的更新以防万一有人会发现 SFINAE 的这种用法有帮助。


UPDATE OLD更新旧的

I found one cumbersome solution involving SFINAE, but it results in defining a class template twice, therefore is very error prone.我找到了一个涉及 SFINAE 的繁琐解决方案,但它会导致定义一个类模板两次,因此非常容易出错。 I don't know if it can be wrapped up with macro in a manner that will make it possible to write it only once.我不知道它是否可以用宏以一种可以只写一次的方式包装起来。

// public.h
#pragma once

#ifndef DLL_BUILD
#define API __declspec(dllimport)
#else 
#define API __declspec(dllexport)
#endif // !DLL_BUILD

#include <type_traits>

namespace templ_export
{
    template <class T>
    struct is_exported : std::false_type {};

    // this can be placed to a separated header (i.e. Exported.hpp)
    template <> struct is_exported<int> : std::true_type {};

    template <class T>
    struct API FooExported
    {
        static T Sum(T a, T b)
        {
            //static_assert(std::is_fundamental_v<T>);
            return a + b;
        }
    };

    template <class T>
    struct FooNotExported
    {
        static T Sum(T a, T b)
        {
            //static_assert(std::is_fundamental_v<T>);
            return a + b;
        }
    };


    template <class T, bool = templ_export::is_exported<T>()>
    struct GetFooExported
    {
        using type = FooNotExported<T>;
    };

    template <class T>
    struct GetFooExported<T, true>
    {
        using type = FooExported<T>;
    };
}


template <class T>
using Foo = typename templ_export::GetFooExported<T>::type;


/////////////////////////////////
// impl.cpp

#include "public.h"


void dummy::be_dummy()
{
    volatile int a = 0;
    return;
}

template struct API templ_export::FooExported<int>;

Here is a simple way of exporting class template instances.这是导出类模板实例的简单方法。

On Dll creation compiler must think, that Foo is defined as dllexport.在创建 Dll 时,编译器必须认为Foo被定义为 dllexport。 But while creating an Executable and linking to that Dll, Foo class template must not have any declspec attributes applied to it.但是在创建可执行文件并链接到该 Dll 时, Foo类模板不得应用任何declspec属性。 Though we need to declare particular class template instances as dllimport .尽管我们需要将特定的类模板实例声明为dllimport

// public.h
#pragma once

#ifndef DLL_BUILD
#define API __declspec(dllimport)
#else 
#define API __declspec(dllexport)
#endif // !DLL_BUILD

// define T_API emplty for library users, hence they will see just 'struct Foo'
#ifndef T_API
#define T_API
#endif

#include <type_traits>

template <class T>
struct T_API Foo
{
    static T Sum(T a, T b)
    {
        //static_assert(std::is_fundamental_v<T>);
        return a + b;
    }
};

// impl.cpp
// Compile with DLL_BUILD defined

// define T_API for library build
#define T_API __declspec(dllexport)

#include "public.h"


void dummy::be_dummy()
{
    volatile int a = 0;
    return;
}

// instantiating class template 
template struct T_API Foo<int>;

For executable:对于可执行文件:

// Exported.h
// this header needs to be shipped alongside with public.h and included after
#pragma once

// declare template instance as imported
template struct __declspec(dllimport) Foo<int>;
// main.cpp 
// Executable linked to library

#include "public.h"
#include "Exported.h"



int main()
{
    dummy().be_dummy();

    // Sum is called from Executable
    std::cout << Foo<double>().Sum(4.12, 3.18) << std::endl;

    // Sum is called from Dll
    std::cout << Foo<int>().Sum(60, 9) << std::endl;


    return 0;
}

I consider this approach to be useful for header-only template libraries.我认为这种方法对仅包含标头的模板库很有用。 Like precompiled headers, dll with class template instances will reduce compilation time.与预编译头文件一样,带有类模板实例的 dll 将减少编译时间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM