使用预处理器定义多个相似的变量C++

Define multiple similar variables with C++ preprocessor

本文关键字:相似 变量 C++ 预处理 处理器 定义      更新时间:2023-10-16

微控制器有许多引脚,每个引脚都定义如下

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

好。
现在我P不需要为 120 个端口调用 120+ 次,每个端口有 32 个引脚。我想看到类似的东西

FOR_EACH( X in A,B,C,D ) 
  FOR_EACH( i in 0..31 ) 
    P(X,i);

请不要建议使用TCL,python等来生成C++代码。
我找到了一个答案,但理解如何在我的情况下使用它很复杂。

主要思想是避免复制粘贴 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);

你没有发布一个真正最小的例子。所以这充其量只是一个猜测。但是如果你使用的是 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> ,依此类推。

真的需要const Leg PA29吗(大概在其他地方参考PA29)?

如果你准备编写PA[29]并且PA属于具有合适operator []的类型,那么你只需要定义PAPBPCPD(我甚至可能不会为此使用预处理器定义)。

或者,升压预处理器库提供循环。

带有 Boost 预处理器库的解决方案:

#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和其他符号下划线作为未解析的符号。