首页 > 解决方案 > 将混合类(模板化和非模板化函数)编译到静态库中

问题描述

为了科学,我正在编写一个游戏引擎库。我过去成功地编写了静态库,尽管没有模板函数。

在处理模板化函数时,我习惯将它们的代码与非模板化的代码分开。模板函数代码位于头文件中,而其他函数代码位于 .cpp/.hpp 文件中。

下面是其中一个模块的片段:信号。

// Connection.h
#pragma once
#include <memory>
#include <functional>

namespace mqs
{
    using Disconnector = std::function<void(std::uint32_t)>;

    class Connection final
    {
    public:
        explicit Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index);

        bool connected() const;
        void disconnect() const;

    private:
        std::uint32_t index;
        std::weak_ptr<mqs::Disconnector> disconnector;
    };
}

// Signal.h
#pragma once
#include <vector>
#include "connection.hpp"

namespace mqs
{
    template <typename...>
    class Signal;

    template <typename R, typename... A>
    class Signal<R(A...)> final
    {
    public:
        Signal();

        template <typename Lambda>
        mqs::Connection connect(Lambda&& lambda) {
            slots.push_back(std::forward<Lambda>(lambda));
            return mqs::Connection(disconnector, slots.size() - 1U);
        }

        void operator()(A&&... args) const;

        unsigned connections() const;

    private:
        std::vector<std::function<R(A...)>> slots;
        std::shared_ptr<mqs::Disconnector> disconnector;
    };
}

// Connection.hpp
#pragma once
#include "connection.h"

namespace mqs
{
    Connection::Connection(std::shared_ptr<mqs::Disconnector> disconnector, std::uint32_t index) {
        this->index = index;
        this->disconnector = disconnector;
    }

    bool Connection::connected() const {
        return !disconnector.expired();
    }

    void Connection::disconnect() const {
        if (const auto& lock = disconnector.lock()) {
            lock->operator()(index);
        }
    }
}

// Signal.hpp
#pragma once
#include "signal.h"

namespace mqs
{
    template <typename R, typename... A>
    Signal<R(A...)>::Signal() {
        disconnector = std::make_shared<mqs::Disconnector>([this](std::uint32_t index) {
            slots.erase(slots.begin() + index);
        });
    }

    template <typename R, typename... A>
    void Signal<R(A...)>::operator()(A&&... args) const {
        for (auto& slot : slots) {
            slot(std::forward<A>(args)...);
        }
    }

    template <typename R, typename... A>
    unsigned Signal<R(A...)>::connections() const {
        return slots.size();
    }
}

它可以编译,但我一直在处理的问题之一是mqs::Signal(signal.hpp) 不能包含在不同的头文件中,否则会导致function already has a body. 当包括在内时,signal.h我明白unresolved external symbol这是有道理的。

我也尝试过inline在上面的 .hpp 文件中定义所有函数。

除了使用巨大的仅标头方法之外,还有什么方法可以实现这一点?

标签: c++templatescompilationstatic-librariesinline

解决方案


正如您已经知道的那样,您需要制作功能模板inline。这是必要的,因为模板首先需要被实例化为可编译的函数,这意味着编译器需要源代码。

但是,如果您查看类似 的成员Signal<R(A...)>::disconnector;,您会注意到它们不依赖于Ror A...。因此,您可以将它们移动到非模板基类。

.ipp对于仍然需要包含的实现文件使用扩展名有一个相当普遍的约定,例如因为它们包含模板代码。这些通常会包含在相应的.hpp文件中,就在#endif标头保护之前。因此,.ipp文件不需要自己的标头保护。


推荐阅读