【问题标题】:c++ static_assert and compile-timec++ static_assert 和编译时
【发布时间】:2017-04-30 19:34:45
【问题描述】:

我想为 ARM 32 位微控制器编写代码,并且想使用现代 C++ 进行教育。我的目标是:可读的代码,在编译时可配置,并帮助编译器生成最大的大小和速度优化。我应该使用什么语言工具来归档我的目标? 我写了一些代码,静态断言不起作用...请帮我修复这段代码(添加断言并且没有开销)。

#include <initializer_list>
#include <vector>

typedef uint port_type;
typedef uint pin_type;

class Pin {
private:
    const port_type _port;
    const pin_type _pin;
public:
    Pin(const port_type port, const pin_type pin) :
            _port(port), _pin(pin) {
    }
};

class Actuator {
private:
    const Pin _enable_pin;
    const Pin _dir_pin;
    const Pin _step_pin;
public:
    Actuator(const Pin enable_pin, const Pin dir_pin, const Pin step_pin) :
            _enable_pin(enable_pin), _dir_pin(dir_pin), _step_pin(step_pin) {
    }

};

class Machine {
private:
    const std::vector<Actuator> _actuators;
public:
    Machine(const std::initializer_list<Actuator> actuators) :
            _actuators(actuators) {
        /*check: all actuators _enable_pin ports are same*/
        /*check: all actuators _dir_pin ports are same*/
        /*check: all actuators _step_pin ports are same*/
        /*check: all port/pin pairs are unique*/
    }
};

int main() {
    /*example: good sequence*/
    Actuator act1(Pin(1, 1), Pin(1, 2), Pin(1, 3));
    Actuator act2(Pin(1, 4), Pin(1, 5), Pin(1, 6));
    Machine machine1( { act1, act2 });
    /*example: bad sequence*/
    Actuator act3(Pin(2, 1), Pin(2, 2), Pin(2, 2)); // NOK! Pin(2,2) already used!
    Actuator act4(Pin(2, 1), Pin(2, 3), Pin(2, 4)); // NOK! Pin(2,1) already used in act3!
    Machine machine2( { act3, act4 });
} 

【问题讨论】:

  • 个人使用enable_if而不是static_assert。原因很简单,您可以默认为“无法实例化错误”,而不是将其放入模板中然后编译失败(您会得到不匹配的替换错误和无数次失败)。

标签: c++ c++11 c++14 compile-time static-assert


【解决方案1】:

大量使用constexprstd::array代替std::vector,并将Machine定义为模板类(其参数为std::array的维度),可以改造Machine的构造函数在运行时抛出异常的constexpr 方法中,或者当对象Machine 定义为constexpr 时,编译时。

以下示例(不幸的是 C++14 但不是 C++11)在编译时截取 act3 中有两个等于 Pins

#include <array>
#include <stdexcept>
#include <initializer_list>

typedef std::size_t port_type;
typedef std::size_t pin_type;

class Pin
 {
   private:
      port_type const _port;
      pin_type const  _pin;

   public:
      constexpr Pin (port_type const port, pin_type const pin)
         : _port{port}, _pin{pin}
       { }

      friend constexpr bool operator== (Pin const & p1, Pin const & p2)
       { return (p1._port == p2._port) && (p1._pin == p2._pin); }
 };

class Actuator
 {
   private:
      Pin const _enable_pin;
      Pin const _dir_pin;
      Pin const _step_pin;

   public:
      constexpr Actuator (Pin const ep, Pin const dp, Pin const sp)
         : _enable_pin{ep}, _dir_pin{dp}, _step_pin{sp}
       { }

      constexpr bool checkColl () const
       { return    (_enable_pin == _dir_pin) || (_enable_pin == _step_pin)
                || (_dir_pin == _step_pin); }
 };

template <std::size_t N>
class Machine
 {
   private:
      std::array<Actuator, N> const _actuators;

   public:
      template <typename ... Args>
      constexpr Machine (Args const & ...  actuators)
         : _actuators{ { actuators ... } }
       {
         static_assert(sizeof...(Args) == N, "!");

         for ( auto ui = 0U ; ui < N ; ++ui )
          {
            if ( _actuators[ui].checkColl() )
               throw std::logic_error("port collision");

            // other checks here
          }
       }
 };

int main()
 {
   constexpr Actuator act1 { Pin{ 1, 1 }, Pin{ 1, 2 }, Pin{ 1, 3 } };
   constexpr Actuator act2 { Pin{ 1, 4 }, Pin{ 1, 5 }, Pin{ 1, 6 } };
   constexpr Machine<2U> machine1 { act1, act2 };

   constexpr Actuator act3 { Pin{ 2, 1 }, Pin{ 2, 2 }, Pin{ 2, 2 } };
   constexpr Actuator act4 { Pin{ 2, 1 }, Pin{ 2, 3 }, Pin{ 2, 4 } };
   constexpr Machine<2U> machine2 { act3, act4 }; // compilation error
 } 

您可以添加其他检查。

【讨论】:

  • 出于好奇,具体c++14位是什么?
  • @n.caillou - 仅(如果我没记错的话)Machine 的模板 constexpr 构造函数的主体:在 C++11 中必须基本上是空的(如果我没记错的话)
  • @max66 感谢您的回答,我尝试编译及其工作,但我的课程中需要非 const 成员。例如我需要在Actuator类中添加max_velocity参数,当然我需要在运行时改变这个参数,但是pin总是必须是const。我怎样才能使这种方式更好(在运行时获得最佳性能)?
  • @Alexey - 我明白了...如果您需要能够修改您的 Actuator 对象,您不能定义它们 constexpr。一个完全不同的想法:Pins 作为模板参数呢?给我一些时间,我会尝试提出一个例子。
  • @Alexy - 添加了另一个完全不同的答案;希望这会有所帮助。
【解决方案2】:

OP 说

我的班级需要非常量成员。例如我需要在 Actuator 类中添加 max_velocity 参数,当然我需要在运行时更改此参数,但引脚必须始终为 const。我怎样才能使这种方式更好(在运行时获得最佳性能)?

我不是最佳性能(运行时或编译时)的专家,但是,如果您需要检查应该修改运行时的对象的编译时间......我能想到的最好的方法是您可以尝试将 const 元素作为类型进行管理。

所以我建议你另一个例子,完全不同,基于Pin&lt;std::size_t, std::size_t&gt;模板结构。

希望对你有帮助

#include <vector>
#include <iostream>

template <typename, typename>
struct PairIsEq : std::false_type
 { };

template <template <std::size_t, std::size_t> class C,
          std::size_t S1, std::size_t S2>
struct PairIsEq<C<S1, S2>, C<S1, S2>> : std::true_type
 { };


template <std::size_t O, std::size_t I>
struct Pin
 { };

template <typename, typename, typename, typename = void>
struct Actuator0;

template <typename EnableP, typename DirP, typename StepP>
struct Actuator0<EnableP, DirP, StepP,
   typename std::enable_if<   (false == PairIsEq<EnableP, DirP>::value)
                           && (false == PairIsEq<EnableP, StepP>::value)
                           && (false == PairIsEq<DirP, StepP>::value)>::type>
 {
   // something modifiable
   std::size_t  max_vel = 0U;
 };

struct ActBase
 { };

template <typename P1, typename P2, typename P3,
          bool =    PairIsEq<P1, P2>::value
                 || PairIsEq<P1, P3>::value
                 || PairIsEq<P2, P3>::value>
struct Actuator;

template <typename EnableP, typename DirP, typename StepP>
struct Actuator<EnableP, DirP, StepP, false> : public ActBase
 {
   // something modifiable
   std::size_t  max_vel = 0U;
 };

template <typename, typename>
struct CheckA : public std::false_type
 { };

template <typename P1a, typename P2a, typename P3a,
          typename P1b, typename P2b, typename P3b>
struct CheckA<Actuator<P1a, P2a, P3a>, Actuator<P1b, P2b, P3b>>
      : public std::integral_constant<bool,
            (false == PairIsEq<P1a, P1b>::value)
         && (false == PairIsEq<P2a, P2b>::value)
         && (false == PairIsEq<P3a, P3b>::value)>
 { };


template <bool, typename ...>
struct CheckActs
 { };

template <typename Act0>
struct CheckActs<true, Act0>
 { using type = void; };

template <typename Act0, typename Act1, typename ... Acts>
struct CheckActs<true, Act0, Act1, Acts...>
      : public CheckActs<CheckA<Act0, Act1>::value, Act0, Acts...>
 {  };


struct Machine
 {
   std::vector<ActBase> const _actuators; 

   template <typename ... Acts,
             typename CheckActs<true, Acts...>::type * = nullptr>
   Machine (Acts const & ... acts) : _actuators{ acts... }
    { }
 };


int main ()
 { 
   // act1 compile (all Pins are different) ...
   Actuator<Pin<1U, 1U>, Pin<1U, 2U>, Pin<1U, 3U>> act1;

   // ...and is modifiable
   act1.max_vel = 1U;

   // act2 compile (all Pins are different)
   Actuator<Pin<1U, 4U>, Pin<1U, 5U>, Pin<1U, 6U>> act2;

   // act3 doesn't compile:  EnableP and StepP are equals
   //Actuator<Pin<2U, 1U>, Pin<2U, 2U>, Pin<2U, 2U>> act3;

   // act4 compile (all Pin are different)
   Actuator<Pin<2U, 1U>, Pin<2U, 3U>, Pin<2U, 4U>> act4;

   // act5 compile (all Pin are different)
   Actuator<Pin<2U, 1U>, Pin<2U, 4U>, Pin<2U, 5U>> act5;

   // mac1 compile: no Pin collisions
   Machine mac1 { act1, act2, act4 };

   // mac2 compilation error: EnablePin collision
   // Machine mac2 { act4, act5 };
 }

【讨论】:

  • 谢谢!它工作正常。我也不是专家,但我会在编译后观看汇编程序生成,而您的“模板魔术”工作。真正的模板是使您的程序小而快的最佳方式。我会努力提高可读性。
  • 您如何看待第一个答案中类中的 const 引用非 const 变量?我正在尝试,但它没有编译。
  • @Alexey - 是的:模板(但也有 constexpr 函数)允许在编译时间做大量工作;它们真的很有用;关于类中的 const 引用......如果它们连接到类之外的变量,我认为它们是危险的(如果类仍然存在时外部变量超出范围);但你应该展示一个真实的例子(也许是一个不同的问题)。
猜你喜欢
  • 1970-01-01
  • 2018-04-26
  • 2012-11-30
  • 1970-01-01
  • 2017-06-30
  • 1970-01-01
  • 1970-01-01
  • 2022-01-24
  • 1970-01-01
相关资源
最近更新 更多