用 C++ 预处理器定义多个相似的变量
Define multiple similar variables with C++ preprocessor
微控制器有很多引脚,每个都定义为
const Leg PA29 { PIOA, BIT(29) }; // struct Pin is already taken
// ... about 120 more: 4 port x 32 pin
我写了一个简单的 define
来制作更短形式的别名
#define P(Port,PinN) \
const Leg P##Port##PinN { PIO##Port, BIT(PinN) }
用作
P(D,2); //produces PD2 with { PIOD, BIT(2) }
不错。
现在我不需要为 4 个端口调用 P
120 次以上,每个端口有 32 个引脚。我想看到类似
的内容
FOR_EACH( X in A,B,C,D ) \
FOR_EACH( i in 0..31 ) \
P(X,i);
请不要建议TCL,python等生成C++代码。
我找到了一个 answer,但在我的案例中理解如何使用它太复杂了。
主要思想是避免复制粘贴120行。所有 120+ 个引脚应在大约 10 行内定义。
更新。 BIT
是如何定义的:
///@param n is value from 0 to number of bits in unsigned value
template<typename UnsignedIntegerT=unsigned>
constexpr UnsignedIntegerT BIT(UnsignedIntegerT n){ return 1<<n; }
upd2。最小示例
///////////////////
// Vendor provides something like:
///////////////////
struct Pio
{
unsigned reg1;
unsigned reg2;
unsigned reg3;
//...
unsigned regN;
};
// values not from datasheet but from lantern
#define PIOA ((Pio*)0xAABB6789)
#define PIOB ((Pio*)0xAABB2569)
#define PIOC ((Pio*)0xAABB2566)
#define PIOD ((Pio*)0xAABB2323)
//...
/////////////
// Kyb's code
/////////////
class Leg
{
public:
Pio *pio;
unsigned pinN;
//methods...
};
///@param n is value from 0 to number of bits in unsigned value
template<typename UnsignedIntegerT=unsigned>
constexpr UnsignedIntegerT BIT(UnsignedIntegerT n){ return 1u<<n; }
//////////////
// Now need to define 120+ pins-legs
// like this
const Leg PA29 { PIOA, BIT(29) };
// or with shortener alias
/// Alias to shortly produce leg definition
/// Example: `P(A,2)` will define `PA2` with proper PIO and bit.
#define P(Port,PinN) \
const Leg P##Port##PinN { PIO##Port, BIT<unsigned>(PinN) }
//P(D,4); //produces PD4
//P(C,3);
你真的需要const Leg PA29
吗(大概指的是其他地方的PA29
)?
如果您准备编写 PA[29]
并且 PA
是具有合适 operator []
的类型,那么您将只需要 PA
、PB
、PC
和 PD
来定义(我什至可能不会为此使用预处理器定义)。
或者,Boost Preprocessor library 提供循环。
你没有post一个真正最小的例子。所以这充其量只是一个猜测。但是如果你使用的是 C++14,并且你的类型是 constexpr 可构造的,那么在我看来你应该尽可能地取消预处理器,而只使用变量模板:
enum port {A, B, C, D};
template<port>
struct PIO_ID;
template<> struct PIO_ID<A>{ static constexpr auto value = PIOA; };
template<> struct PIO_ID<B>{ static constexpr auto value = PIOB; };
template<> struct PIO_ID<C>{ static constexpr auto value = PIOC; };
template<> struct PIO_ID<D>{ static constexpr auto value = PIOD; };
template<port P>
constexpr auto PIO = PIO_ID<P>::value;
template<port PORT, int PIN_NUM>
constexpr Leg P{ PIO<PORT> , BIT(PIN_NUM) };
就是这样。现在您可以将这些常量称为 P<A, 0>
,等等。
Boost preprocessor library的解决方案:
#include "Pio.hpp" // Has Leg and BIT definitions
#include "boost/preprocessor.hpp" //#include "boost/preprocessor/iteration"
/// Alias to shortly produce leg definition
/// The P(A,2) will define PA2 with proper PIO and bit.
#define P(Port,PinN) \
const Leg P##Port##PinN { PIO##Port, BIT<unsigned>(PinN) }
#define PA(n) P(A,n)
#define PB(n) P(B,n)
#define PC(n) P(C,n)
#define PD(n) P(D,n)
#define BOOST_PP_LOCAL_MACRO(n) PA(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE() //??=include BOOST_PP_LOCAL_ITERATE() //-Wtrigraphs
#define BOOST_PP_LOCAL_MACRO(n) PB(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE()
#define BOOST_PP_LOCAL_MACRO(n) PC(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE()
#define BOOST_PP_LOCAL_MACRO(n) PD(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE()
这很好用。但有一点不便:IDE 会在 PA5
和其他符号下划线作为未解析的符号。
微控制器有很多引脚,每个都定义为
const Leg PA29 { PIOA, BIT(29) }; // struct Pin is already taken
// ... about 120 more: 4 port x 32 pin
我写了一个简单的 define
来制作更短形式的别名
#define P(Port,PinN) \
const Leg P##Port##PinN { PIO##Port, BIT(PinN) }
用作
P(D,2); //produces PD2 with { PIOD, BIT(2) }
不错。
现在我不需要为 4 个端口调用 P
120 次以上,每个端口有 32 个引脚。我想看到类似
FOR_EACH( X in A,B,C,D ) \
FOR_EACH( i in 0..31 ) \
P(X,i);
请不要建议TCL,python等生成C++代码。
我找到了一个 answer,但在我的案例中理解如何使用它太复杂了。
主要思想是避免复制粘贴120行。所有 120+ 个引脚应在大约 10 行内定义。
更新。 BIT
是如何定义的:
///@param n is value from 0 to number of bits in unsigned value
template<typename UnsignedIntegerT=unsigned>
constexpr UnsignedIntegerT BIT(UnsignedIntegerT n){ return 1<<n; }
upd2。最小示例
///////////////////
// Vendor provides something like:
///////////////////
struct Pio
{
unsigned reg1;
unsigned reg2;
unsigned reg3;
//...
unsigned regN;
};
// values not from datasheet but from lantern
#define PIOA ((Pio*)0xAABB6789)
#define PIOB ((Pio*)0xAABB2569)
#define PIOC ((Pio*)0xAABB2566)
#define PIOD ((Pio*)0xAABB2323)
//...
/////////////
// Kyb's code
/////////////
class Leg
{
public:
Pio *pio;
unsigned pinN;
//methods...
};
///@param n is value from 0 to number of bits in unsigned value
template<typename UnsignedIntegerT=unsigned>
constexpr UnsignedIntegerT BIT(UnsignedIntegerT n){ return 1u<<n; }
//////////////
// Now need to define 120+ pins-legs
// like this
const Leg PA29 { PIOA, BIT(29) };
// or with shortener alias
/// Alias to shortly produce leg definition
/// Example: `P(A,2)` will define `PA2` with proper PIO and bit.
#define P(Port,PinN) \
const Leg P##Port##PinN { PIO##Port, BIT<unsigned>(PinN) }
//P(D,4); //produces PD4
//P(C,3);
你真的需要const Leg PA29
吗(大概指的是其他地方的PA29
)?
如果您准备编写 PA[29]
并且 PA
是具有合适 operator []
的类型,那么您将只需要 PA
、PB
、PC
和 PD
来定义(我什至可能不会为此使用预处理器定义)。
或者,Boost Preprocessor library 提供循环。
你没有post一个真正最小的例子。所以这充其量只是一个猜测。但是如果你使用的是 C++14,并且你的类型是 constexpr 可构造的,那么在我看来你应该尽可能地取消预处理器,而只使用变量模板:
enum port {A, B, C, D};
template<port>
struct PIO_ID;
template<> struct PIO_ID<A>{ static constexpr auto value = PIOA; };
template<> struct PIO_ID<B>{ static constexpr auto value = PIOB; };
template<> struct PIO_ID<C>{ static constexpr auto value = PIOC; };
template<> struct PIO_ID<D>{ static constexpr auto value = PIOD; };
template<port P>
constexpr auto PIO = PIO_ID<P>::value;
template<port PORT, int PIN_NUM>
constexpr Leg P{ PIO<PORT> , BIT(PIN_NUM) };
就是这样。现在您可以将这些常量称为 P<A, 0>
,等等。
Boost preprocessor library的解决方案:
#include "Pio.hpp" // Has Leg and BIT definitions
#include "boost/preprocessor.hpp" //#include "boost/preprocessor/iteration"
/// Alias to shortly produce leg definition
/// The P(A,2) will define PA2 with proper PIO and bit.
#define P(Port,PinN) \
const Leg P##Port##PinN { PIO##Port, BIT<unsigned>(PinN) }
#define PA(n) P(A,n)
#define PB(n) P(B,n)
#define PC(n) P(C,n)
#define PD(n) P(D,n)
#define BOOST_PP_LOCAL_MACRO(n) PA(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE() //??=include BOOST_PP_LOCAL_ITERATE() //-Wtrigraphs
#define BOOST_PP_LOCAL_MACRO(n) PB(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE()
#define BOOST_PP_LOCAL_MACRO(n) PC(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE()
#define BOOST_PP_LOCAL_MACRO(n) PD(n);
#define BOOST_PP_LOCAL_LIMITS (0, 31)
#include BOOST_PP_LOCAL_ITERATE()
这很好用。但有一点不便:IDE 会在 PA5
和其他符号下划线作为未解析的符号。