c++ static_assert 和编译时

c++ static_assert and compile-time

我想为 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 });
} 

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

以下示例(不幸的是 C++14 而不是 C++11)拦截,编译时,在 act3

中有两个等于 Pin
#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
 } 

您可以添加其他检查。

OP 说

I need non const members in my classes. For example I need to add max_velocity parameter to Actuator class, of course I need to change this parameter in runtime, but pins always must be const. How can I make this better way (arhieve best perfomance in runtime)?

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

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

希望对您有所帮助

#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 };
 }