是否可以在编译时执行字符串到 int 的映射?

Mar*_* R. 5 c++

是否可以在编译时执行唯一的字符串到 int 映射?\n假设我有一个这样的模板用于分析:

\n\n
template <int profilingID>\nclass Profile{\npublic:\n    Profile(){ /* start timer */ }\n    ~Profile(){ /* stop timer */ }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

我将其放在函数调用的开头,如下所示:

\n\n
void myFunction(){\n    Profile<0> profile_me;\n\n    /* some computations here */\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在我正在尝试做类似以下的事情,这是不可能的,因为字符串文字不能用作模板参数

\n\n
void myFunction(){\n    Profile<"myFunction"> profile_me; // or PROFILE("myFunction")\n\n    /* some computations here */\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

我可以声明全局变量来解决这个问题,但我认为避免以前的声明会更优雅。表格的简单映射

\n\n
    \n
  • \xe2\x80\x9dmyFunction\xe2\x80\x9d \xe2\x86\x92 0
  • \n
  • \xe2\x80\x9dmyFunction1\xe2\x80\x9d \xe2\x86\x92 1
  • \n
  • \xe2\x80\xa6
  • \n
  • \xe2\x80\x9dmyFunctionN\xe2\x80\x9d \xe2\x86\x92 N
  • \n
\n\n

就足够了。但到目前为止,无论使用 constexpr、模板元编程还是宏,我都无法找到完成此类映射的方法。有任何想法吗?

\n

5go*_*der 0

正如@harmic在评论中已经提到的,您可能应该将名称传递给构造函数。这也可能有助于减少代码膨胀,因为您不必为每个函数生成新类型。

然而,我不想错过展示一个肮脏的黑客的机会,这在字符串无法传递给构造函数的情况下可能有用。如果字符串的最大长度在编译时已知,则可以将它们编码为整数。在下面的示例中,我仅使用单个整数,它将系统上的最大字符串长度限制为 8 个字符。将方法扩展到多个整数(通过小宏方便地隐藏分割逻辑)留给读者作为练习。

该代码利用 C++14 功能在constexpr函数中使用任意控制结构。在 C++11 中,您必须编写wrap稍微不那么直接的递归函数。

#include <climits>
#include <cstdint>
#include <cstdio>
#include <type_traits>

template <typename T = std::uintmax_t>
constexpr std::enable_if_t<std::is_integral<T>::value, T>
wrap(const char *const string) noexcept
{
  constexpr auto N = sizeof(T);
  T n {};
  std::size_t i {};
  while (string[i] && i < N)
    n = (n << CHAR_BIT) | string[i++];
  return (n << (N - i) * CHAR_BIT);
}

template <typename T>
std::enable_if_t<std::is_integral<T>::value>
unwrap(const T n, char *const buffer) noexcept
{
  constexpr auto N = sizeof(T);
  constexpr auto lastbyte = static_cast<char>(~0);
  for (std::size_t i = 0UL; i < N; ++i)
    buffer[i] = ((n >> (N - i - 1) * CHAR_BIT) & lastbyte);
  buffer[N] = '\0';
}

template <std::uintmax_t Id>
struct Profile
{
  char name[sizeof(std::uintmax_t) + 1];

  Profile()
  {
    unwrap(Id, name);
    std::printf("%-8s %s\n", "ENTER", name);
  }

  ~Profile()
  {
    std::printf("%-8s %s\n", "EXIT", name);
  }
};
Run Code Online (Sandbox Code Playgroud)

它可以这样使用:

void
function()
{
  const Profile<wrap("function")> profiler {};
}

int
main()
{
  const Profile<wrap("main")> profiler {};
  function();
}
Run Code Online (Sandbox Code Playgroud)

输出:

#include <climits>
#include <cstdint>
#include <cstdio>
#include <type_traits>

template <typename T = std::uintmax_t>
constexpr std::enable_if_t<std::is_integral<T>::value, T>
wrap(const char *const string) noexcept
{
  constexpr auto N = sizeof(T);
  T n {};
  std::size_t i {};
  while (string[i] && i < N)
    n = (n << CHAR_BIT) | string[i++];
  return (n << (N - i) * CHAR_BIT);
}

template <typename T>
std::enable_if_t<std::is_integral<T>::value>
unwrap(const T n, char *const buffer) noexcept
{
  constexpr auto N = sizeof(T);
  constexpr auto lastbyte = static_cast<char>(~0);
  for (std::size_t i = 0UL; i < N; ++i)
    buffer[i] = ((n >> (N - i - 1) * CHAR_BIT) & lastbyte);
  buffer[N] = '\0';
}

template <std::uintmax_t Id>
struct Profile
{
  char name[sizeof(std::uintmax_t) + 1];

  Profile()
  {
    unwrap(Id, name);
    std::printf("%-8s %s\n", "ENTER", name);
  }

  ~Profile()
  {
    std::printf("%-8s %s\n", "EXIT", name);
  }
};
Run Code Online (Sandbox Code Playgroud)