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 });
}
大量使用constexpr
,std::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 };
}
我想为 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 });
}
大量使用constexpr
,std::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 };
}