简体   繁体   English

我可以在编译时检测到编译时常量的“函数参数”

[英]Can I detect at compile time “function arguments” that are compile-time constants

Can I detect at compile time whether "function arguments" 1 are compile-time constants? 我可以在编译时检测“函数参数” 1是否是编译时常量?

For example a function print(int i) that can print "constant 5" if called as print(5) but "non-constant 5" if called as print(i) where i is some non-constant variable. 例如,一个功能print(int i)可打印"constant 5"如果被称为print(5)"non-constant 5"如果被称为print(i)其中, i是一些非恒定变量。 In particular, in the "is constant" branch I should be able to treat i as a constexpr, including using it for template arguments, etc. 特别是,在“is constant”分支中,我应该能够将i视为constexpr,包括将其用于模板参数等。

Macro tricks, template meta-programming and SFINAE tricks are all OK. 宏技巧,模板元编程和SFINAE技巧都可以。 Ideally it is portable, but solutions that are compiler-specific are better than nothing. 理想情况下它是可移植的,但是编译器特定的解决方案总比没有好。

It's OK if there are "false negatives" - ie, if constant values are sometimes detected as non-constant (eg, when certain optimizations are disabled). 如果存在“错误否定”则可以 - 即,如果常量值有时被检测为非常数(例如,禁用某些优化时)。

Bonus points if the solution can detect when constant values are indirectly passed to the function (eg, when a constant value is passed to an intermediate function that calls print and which is subsequently inlined exposing the constant to print ). 如果解决方案可以检测到何时将常量值间接传递给函数(例如,当常量值传递给调用print的中间函数并且随后内联时将常量暴露给print ),则可以获得奖励积分。 This last behavior is evidently optimization dependent. 最后一种行为显然取决于优化。

Double bonus points if it naturally extends to multiple arguments. 如果它自然延伸到多个参数,则可获得双倍奖励

If one could have overloaded versions of functions with and without constexpr arguments this would presumably be straightforward, but you can't . 如果一个人可以在有和没有constexpr参数的情况下重载函数的版本,这可能是直截了当的,但你不能


1 I'm putting "function arguments" in quotes here because the solution doesn't strictly require detecting this state within a function (or at the caller/callee boundary with special arguments) - it just has to appear to the caller like a function but macros or other tricks like a static object with operator() etc could be used. 1我在这里引用“函数参数”,因为解决方案并不严格要求在函数内(或在具有特殊参数的调用者/被调用者边界)检测此状态 - 它只需要像函数一样出现给调用者但是可以使用宏或其他技巧,例如带有operator()等的静态对象。

it doesn't have to be a plain function void print(int i) - it could be a function-like macro that does some magic on its arguments and calls a different function depending on whether it is a constant, or it could be some template magic 它不必是一个普通的函数void print(int i) - 它可以是一个类似函数的宏,它在它的参数上做一些魔术,并根据它是一个常量调用一个不同的函数,或者它可能是一些模板魔术

"function-like macro" you said? “功能像宏”你说?

Well... first of all I have to warn you that C-style function-like macro are dangerous. 嗯......首先,我必须警告你,C风格的功能宏是危险的。 Distilled evil, IMHO. 蒸馏邪恶,恕我直言。

Said this, if you really accept a macro based solution, I suppose that combining it with constexpr methods, a template struct , static local variables and SFINAE... 说这个,如果你真的接受基于宏的解决方案,我constexpr它与constexpr方法,模板structstatic局部变量和SFINAE结合起来......

If you define the following template PrintStruct struct 如果您定义以下模板PrintStruct struct

template <typename T>
struct PrintStruct
 {
   template <bool>
   static void func (...) 
    { std::cout << "func non-const: " << T::func(true) << std::endl; }

   template <bool b, int I = T::func(b)>
   static void func (int) 
    { std::cout << "func const:     " << I << std::endl; }
 };

and the following C-style function-like macro, that define a foo local struct and pass it, as template argument, to PrintStruct to activate SFINAE to select the desired func() (and call func() , obviously) [ EDIT : macro improved by jxh to make it expand as a statement; 和下面的C风格的函数式宏,定义一个foo本地struct并将其作为模板参数传递给PrintStruct以激活SFINAE以选择所需的func() (并显然调用func() )[ 编辑 :宏由jxh改进,使其作为声明扩展; thanks!] [ EDIT 2 : macro modified, following an observation of the OP, to accept expressions] 谢谢!] [ 编辑2 :宏观修改,观察OP后,接受表达式]

#define Print(i)                          \
[&]()                                     \
 {                                        \
   static int const printLocalVar { i };  \
                                          \
   struct local_foo                       \
    {                                     \
      static constexpr int func (bool b)  \
       { return b ? printLocalVar : 0; }  \
    } ;                                   \
                                          \
   PrintStruct<local_foo>::func<true>(0); \
 }                                        \
()

Observe that the printed value, in the const version of PrintStruct::func() , is a template integer value; 观察到PrintStruct::func()的const版本中的打印值是模板整数值; so can be used also for template arguments, C-style array dimensions, static_assert() s tests, etc. 所以也可以用于模板参数,C风格的数组维度, static_assert()的测试等。

Not sure that is perfectly standard (I'm not a really expert) and that do what you exactly want, but the following is a full working example 不确定这是完全标准的(我不是一个真正的专家),并做你想要的,但以下是一个完整的工作示例

#include <iostream>

template <typename T>
struct PrintStruct
 {
   template <bool>
   static void func (...) 
    { std::cout << "func non-const: " << T::func(true) << std::endl; }

   template <bool b, int I = T::func(b)>
   static void func (int) 
    { std::cout << "func const:     " << I << std::endl; }
 };


#define Print(i)                          \
[&]()                                     \
 {                                        \
   static int const printLocalVar { i };  \
                                          \
   struct local_foo                       \
    {                                     \
      static constexpr int func (bool b)  \
       { return b ? printLocalVar : 0; }  \
    } ;                                   \
                                          \
   PrintStruct<local_foo>::func<true>(0); \
 }                                        \
()

int main()
 {
   constexpr int  i { 2 };
   int const      j { 3 };
   int            k { 4 };
   int const      l { k+1 };

   Print(1);    // print func const:     1
   Print(i);    // print func const:     2
   Print(j);    // print func const:     3
   Print(k);    // print func non-const: 4
   Print(l);    // print func non-const: 5
   Print(2+2);  // print func const:     4
 }

For detecting the constexpr suitability one may consider this GCC-only suggestion from @JohannesSchaub-litb (see linked answer for limitations): 为了检测constexpr适用性,我们可以考虑来自@ JohannesSchaub-litb的这个仅限GCC的 建议 (参见链接的限制答案):

template<typename T> 
constexpr typename remove_reference<T>::type makeprval(T && t) {
  return t;
}

A working example with different cases reads 一个包含不同案例的工作示例

#include <iostream>

////////////////////////////////////////////////////////////////////////////////

// https://stackoverflow.com/a/13305072/2615118

template<class T>
constexpr std::remove_reference_t<T> makeprval(T&& t) {
  return t;
}

#define isprvalconstexpr(e) noexcept(makeprval(e))

////////////////////////////////////////////////////////////////////////////////

template<bool is_constexpr, class Lambda>
struct HybridArg {
  using T = std::invoke_result_t<Lambda>;

  Lambda lambda_;
  constexpr operator T() const { return lambda_(); }// implicit conversion
};

template<bool is_constexpr, class Lambda>
constexpr auto make_hybrid_arg(Lambda lambda) {
  return HybridArg<is_constexpr, Lambda>{lambda};
}

#define WRAP_ARG(arg)                     \
  make_hybrid_arg<isprvalconstexpr(arg)>( \
    [&] { return arg; }                   \
  )                                       \

////////////////////////////////////////////////////////////////////////////////

template<int i>
void print_impl_constexpr() {
  std::cout << i << ": ";
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

void print_impl_fallback(int i) {
  std::cout << i << ": ";
  std::cout << __PRETTY_FUNCTION__ << std::endl;
}

////////////////////////////////////////////////////////////////////////////////

// option 1 (for choosing implementation):
// compile-time introspection

template<class Arg>
struct is_constexpr_arg : std::false_type {};

template<class Lambda>
struct is_constexpr_arg<
  HybridArg<true, Lambda>
> : std::true_type {};

template<class Arg>
void print_by_introspection(Arg arg) {
  if constexpr(is_constexpr_arg<Arg>{}) {
    print_impl_constexpr<arg>();
  }
  else {
    print_impl_fallback(arg);
  }
}

////////////////////////////////////////////////////////////////////////////////

// option 2 (for choosing implementation):
// overload

void print_by_overload(int arg) {
  print_impl_fallback(arg);
}

template<class Lambda>
void print_by_overload(HybridArg<true, Lambda> arg) {
  print_impl_constexpr<arg>();
}

////////////////////////////////////////////////////////////////////////////////

template<class Arg>
void indirection(Arg arg) {
  print_by_introspection(arg);
  print_by_overload(arg);
}

void bad_indirection(int arg) {
  print_by_introspection(arg);
  print_by_overload(arg);
}

////////////////////////////////////////////////////////////////////////////////

int main() {
  {
    int i = 0;
    indirection(i);
  }
  {
    int i = 1;
    indirection(WRAP_ARG(i));
  }
  {
    constexpr int i = 2;
    indirection(WRAP_ARG(i));
  }
  {
    constexpr int i = 3;
    bad_indirection(WRAP_ARG(i));
  }
}

Output after compilation with GCC: 使用GCC编译后的输出:

0: void print_impl_fallback(int)
0: void print_impl_fallback(int)
1: void print_impl_fallback(int)
1: void print_impl_fallback(int)
2: void print_impl_constexpr() [with int i = 2]
2: void print_impl_constexpr() [with int i = 2]
3: void print_impl_fallback(int)
3: void print_impl_fallback(int)

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

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