【问题标题】:hide template function from header file从头文件中隐藏模板函数
【发布时间】:2021-06-09 18:45:31
【问题描述】:

我有以下用例。

Messages.h

template <typename Message>
Message deserialize(std::string const &buf);

template <>
RequestHeader deserialize(std::string const &buf);

template <typename Message>
std::string serialize(Message const &msg);

#include "Messages.inl"

Messages.inl

template <typename Message>
Message deserialize(std::string const &buf, bool partial)
{
    Message msg;
    size_t bufSize = buf.length();
    size_t msgSize = sizeof msg;

    if (msgSize > bufSize)
        throw std::underflow_error("Message size exceeds buffer size.");

    if (msgSize < bufSize && !partial)
        throw std::overflow_error("Buffer size exceeds message size.");

    std::memcpy(&msg, &buf[0], (msgSize < bufSize) ? msgSize : bufSize);
    return msg;
}

template <typename Message>
Message deserialize(std::string const &buf)
{
    return deserialize<Message>(buf, false);
}

template <typename Message>
std::string serialize(Message const &msg)
{
    auto ptr = reinterpret_cast<const char*>(&msg);
    return std::string(ptr, ptr + sizeof msg);
}

Messages.cpp

#include "Messages.h"

template <>
RequestHeader deserialize(std::string const &buf)
{
    return deserialize<RequestHeader>(buf, true);
}

此代码的“问题”是带有签名的模板

Message deserialize(std::string const &buf, bool partial)

可通过任何使用Messages.h 的单元在标题中包含*.inl 文件来公开获得。 但是,它只是泛化反序列化的内部助手,不应公开提供。如果它是常规函数而不是模板,我会将其标记为静态,以便仅在翻译单元中可见。但这似乎对内联文件中的模板没有影响。 我将如何从头文件中隐藏这个模板并仍然从中派生其他模板函数和特化?有没有可能?

【问题讨论】:

  • 目前尚不清楚助手是否在一个文件中使用,仅(这是static 的建议)。如果是这种情况,那么只需在该文件中定义帮助程序。
  • helper目前只在Messages.inl中使用,它又包含在Messages.h中,这是我不想暴露helper函数的外部接口。
  • C++20 通过模块使这成为可能:帮助器可以不用于名称查找,即使它们(必须)可用于实例化。
  • @DavisHerring 感谢您的评论,我也添加了关于 C++20 模块的评论。您知道能够处理 C++ 模块的在线编译器吗?
  • @2b-t:编译器资源管理器有几个(处于不同的完成阶段)。

标签: c++ templates static c++17


【解决方案1】:

在 C++20 之前你通常只有两种选择:

  • 您可以通过将辅助函数移动到 命名空间 来“隐藏”它们,例如 detailshelper。这样一来,人们不会立即看到它,但仍然可以访问它们 (Try it here!):

    namespace details {
      template <typename Message>
      Message deserialize(std::string const &buf, bool partial) {
        Message msg;
        size_t bufSize = buf.length();
        size_t msgSize = sizeof msg;
    
        if (msgSize > bufSize)
          throw std::underflow_error("Message size exceeds buffer size.");
    
        if (msgSize < bufSize && !partial)
          throw std::overflow_error("Buffer size exceeds message size.");
    
        std::memcpy(&msg, &buf[0], (msgSize < bufSize) ? msgSize : bufSize);
        return msg;
     }
    }
    
    template <typename Message>
    Message deserialize(std::string const &buf) {
      return details::deserialize<Message>(buf, false);
    }
    
  • 或者您将函数作为静态方法放在struct(或class)中,从而生成接口public 和辅助函数protected。 (Try it here!)

    struct message {
      public:
        template <typename Message>
        static Message deserialize(std::string const &buf);
    
        template <typename Message>
        static std::string serialize(Message const &msg);
    
      protected:
        template <typename Message>
        static Message deserialize(std::string const &buf, bool partial);
    };
    
    template <typename Message>
    Message message::deserialize(std::string const &buf) {
      return deserialize<Message>(buf, false);
    }
    
    template <>
    RequestHeader message::deserialize(std::string const &buf) {
      return deserialize<RequestHeader>(buf, true);
    }
    

    在您的情况下,您可以从 type_traits 中删除带有 std::is_same_v&lt;T1,T2&gt; 的专业化,或者在更复杂的条件下额外使用 if constexpr (Try it here!)。

如果函数和上面的版本一样简单,我实际上会尝试std::is_same_v&lt;T1,T2&gt; 完全消除辅助函数,如下所示 (Try it here!)

  template <typename Message>
  Message deserialize(std::string const &buf) {
    constexpr bool partial = std::is_same_v<Message,RequestHeader>;

    Message msg;
    size_t bufSize = buf.length();
    size_t msgSize = sizeof msg;

    if (msgSize > bufSize)
      throw std::underflow_error("Message size exceeds buffer size.");

    if (msgSize < bufSize && !partial)
      throw std::overflow_error("Buffer size exceeds message size.");

    std::memcpy(&msg, &buf[0], (msgSize < bufSize) ? msgSize : bufSize);
    return msg;
  }

正如 C++20 中的评论所指出的,您可以使用 modules。 为此,您可以创建一个模块 some_module.cppm 来保存所有功能并仅使用 export 标记接口。

// some_module.cppm
export module some_module;

template <typename T>
auto invisible_helper(T t) {
  return t*t;
}        

export
template <typename T>
auto visible_function(T t) { 
    return invisible_helper(t);
}

在下面的情况下,只有函数 visible_function 被导出,并且可以在 main.cpp 内部访问,而 invisible_helper 仍然隐藏:

// main.cpp
#include <cstdlib>
#include <iostream>
import some_module;

int main() {
  std::cout << visible_function(2.0) << std::endl;
  return EXIT_SUCCESS;
}

如果您安装了clang++12,您可以使用以下三个命令编译代码:

clang++ -std=c++2b -fmodules-ts --precompile some_module.cppm -o some_module.pcm
clang++ -std=c++2b -fmodules-ts -c some_module.pcm -o some_module.o
clang++ -std=c++2b -fmodules-ts -fprebuilt-module-path=. some_module.o main.cpp -o main
./main

【讨论】:

    猜你喜欢
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-24
    • 1970-01-01
    相关资源
    最近更新 更多