Search code examples
c++c++11c++14compile-timestatic-assert

c++ static_assert and compile-time


I want to write for ARM 32-bit microcontroller and I want to use modern C++ for education. My goals is: readable code, configurable in compile-time and help compiler to generate maximum optimizations for size and speed. What language tools should I use to archive my goals? I write some code and static assertions does not work... Please help me to fix this code (add assertions and no overhead).

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

Solution

  • Using a lot of constexpr, std::array instead of std::vector and defining Machine as a template class (whose parameter is the dimension of the std::array), you can transform the constructor of Machine in a constexpr method that throw an exception at runtime or, when the object Machine is defined constexpr, compiletime.

    The following example (unfortunately C++14 but not C++11) intercept, compile-time, that there are two equals Pins in act3

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

    You can add the other checks.