我一直在C++中实现一个可编程逻辑模块库(即和门、或门、RS触发器和TON、TOF和TP定时器)。我决定用实现公共接口的C++类对所有这些逻辑块进行建模。
namespace LogicBlocks
{
class LogicBlk {
public:
enum LogicType_e{
POS, // positive logic
NEG // negative logic
};
virtual void Update(void) = 0;
private:
};
}我已经实现了定时器和RS触发器逻辑块。我现在要做的是实现AND和OR门。我的想法是使用和门和或门与1输入最多8个输入。我以下列方式执行了这一规定。每种类型的门我都有特殊的课程。例如:
具有两个输入的OR门
接口:
namespace LogicBlocks
{
// OR logic gate with two inputs
class Or_02 : public LogicBlk{
public:
Or_02(uint32_t *bitsArray,
uint32_t input_01, LogicType_e inputType_01,
uint32_t input_02, LogicType_e inputType_02,
uint32_t output);
virtual ~Or_02();
void Update(void);
private:
uint32_t m_In01;
LogicType_e m_In01Type;
uint32_t m_In02;
LogicType_e m_In02Type;
uint32_t m_Out;
uint32_t *m_BitsArray;
};
}执行情况:
LogicBlocks::Or_02::Or_02(uint32_t *bitsArray,
uint32_t input_01, LogicType_e inputType_01,
uint32_t input_02, LogicType_e inputType_02,
uint32_t output):
m_BitsArray{bitsArray},
m_In01{input_01}, m_In01Type{inputType_01},
m_In02{input_02}, m_In02Type{inputType_02},
m_Out{output}{
}
LogicBlocks::Or_02::~Or_02() {
}
void LogicBlocks::Or_02::Update(void){
if(((Utils::TestBitSet(m_BitsArray, m_In01) && m_In01Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In01) && m_In01Type == NEG)) ||
((Utils::TestBitSet(m_BitsArray, m_In02) && m_In02Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In02) && m_In02Type == NEG))){
Utils::SetBit(m_BitsArray, m_Out);
}else{
Utils::ClrBit(m_BitsArray, m_Out);
}
}具有三个输入的<#>OR门
接口:
namespace LogicBlocks
{
// OR logic gate with three inputs
class Or_03 : public LogicBlk{
public:
Or_03(uint32_t* const bitsArray,
const uint32_t input_01, const LogicType_e inputType_01,
const uint32_t input_02, const LogicType_e inputType_02,
const uint32_t input_03, const LogicType_e inputType_03,
const uint32_t output);
virtual ~Or_03();
void Update(void);
private:
uint32_t m_In01;
LogicType_e m_In01Type;
uint32_t m_In02;
LogicType_e m_In02Type;
uint32_t m_In03;
LogicType_e m_In03Type;
uint32_t m_Out;
uint32_t *m_BitsArray;
};
} 执行情况:
LogicBlocks::Or_03::Or_03(uint32_t* const bitsArray,
const uint32_t input_01, const LogicType_e inputType_01,
const uint32_t input_02, const LogicType_e inputType_02,
const uint32_t input_03, const LogicType_e inputType_03,
const uint32_t out):
m_BitsArray{bitsArray},
m_In01{input_01}, m_In01Type{inputType_01},
m_In02{input_02}, m_In02Type{inputType_02},
m_In03{input_03}, m_In03Type{inputType_03},
m_Out{output}{
}
LogicBlocks::Or_03::~Or_03() {
}
void LogicBlocks::Or_03::Update(void){
if(((Utils::TestBitSet(m_BitsArray, m_In01) && m_In01Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In01) && m_In01Type == NEG)) ||
((Utils::TestBitSet(m_BitsArray, m_In02) && m_In02Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In02) && m_In02Type == NEG)) ||
((Utils::TestBitSet(m_BitsArray, m_In03) && m_In03Type == POS) || (Utils::TestBitClr(m_BitsArray, m_In03) && m_In03Type == NEG))){
Utils::SetBit(m_BitsArray, m_Out);
}else{
Utils::ClrBit(m_BitsArray, m_Out);
}
}这是用法。假设我有一个名为Logic的对象:
接口:
#include
#include "LogicBlk.h"
namespace Logic
{
class Logic{
public:
Logic();
virtual ~Logic();
// method shall be called from a task
void Loop(void);
private:
uint32_t m_BitsArray[1] = {0};
static const uint8_t NO_LOGIC_BLKS = 2;
LogicBlocks::LogicBlk* m_LogicBlks[NO_LOGIC_BLKS];
};
}执行情况:
#include "Logic.h"
#include "Bits.h"
#include "Or_02.h"
#include "And_02.h"
#define LW_01 (0)
// Byte 01
#define LSig01 (LW_01*32 + 0x00)
#define LSig02 (LW_01*32 + 0x01)
#define LSig03 (LW_01*32 + 0x02)
//efine L (LW_01*32 + 0x03)
//efine L (LW_01*32 + 0x04)
//efine L (LW_01*32 + 0x05)
//efine L (LW_01*32 + 0x06)
//efine L (LW_01*32 + 0x07)
// Byte 02
#define LAx01 (LW_01*32 + 0x08)
#define LAx02 (LW_01*32 + 0x09)
//efine L (LW_01*32 + 0x0A)
//efine L (LW_01*32 + 0x0B)
//efine L (LW_01*32 + 0x0C)
//efine L (LW_01*32 + 0x0D)
//efine L (LW_01*32 + 0x0E)
//efine L (LW_01*32 + 0x0F)
// Byte 03
//efine L (LW_01*32 + 0x10)
//efine L (LW_01*32 + 0x11)
//efine L (LW_01*32 + 0x12)
//efine L (LW_01*32 + 0x13)
//efine L (LW_01*32 + 0x14)
//efine L (LW_01*32 + 0x15)
//efine L (LW_01*32 + 0x16)
//efine L (LW_01*32 + 0x17)
// Byte 04
//efine L (LW_01*32 + 0x18)
//efine L (LW_01*32 + 0x19)
//efine L (LW_01*32 + 0x1A)
//efine L (LW_01*32 + 0x1B)
//efine L (LW_01*32 + 0x1C)
//efine L (LW_01*32 + 0x1D)
//efine L (LW_01*32 + 0x1E)
//efine L (LW_01*32 + 0x1F)
Logic::Logic::Logic(){
m_LogicBlks[0] = new LogicBlocks::Or_02(m_BitsArray,
LSig01, LogicBlocks::LogicBlk::POS,
LSig02, LogicBlocks::LogicBlk::POS,
LAx01);
m_LogicBlks[1] = new LogicBlocks::And_02(m_BitsArray,
LAx01, LogicBlocks::LogicBlk::POS,
LSig03, LogicBlocks::LogicBlk::NEG,
LAx02);
}
Logic::Logic::~Logic(){
}
void Logic::Logic::Loop(void){
for(uint8_t curBlk = 0; curBlk < NO_LOGIC_BLKS; curBlk++){
m_LogicBlks[curBlk]->Update();
}
}为了完整性,实用程序函数用于处理单个位。
bool Utils::TestBitSet(uint32_t *bitsArray, uint32_t bit){
uint32_t wordValue = *(bitsArray + (bit >> 5));
uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));
return ((wordValue & ((uint32_t)1 << bitPosInWord)) >> bitPosInWord) ? true : false;
}
bool Utils::TestBitClr(uint32_t *bitsArray, uint32_t bit){
uint32_t wordValue = *(bitsArray + (bit >> 5));
uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));
return ((wordValue & ((uint32_t)1 << bitPosInWord)) >> bitPosInWord) ? false : true;
}
void Utils::SetBit(uint32_t *bitsArray, uint32_t bit){
uint32_t word = (bit >> 5);
uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));
*(bitsArray + word) |= ((uint32_t)1 << bitPosInWord);
}
void Utils::ClrBit(uint32_t *bitsArray, uint32_t bit){
uint32_t word = (bit >> 5);
uint32_t bitPosInWord = (bit - ((bit >> 5) << 5));
*(bitsArray + word) &= ~((uint32_t)1 << bitPosInWord);
}
void Utils::NegBit(uint32_t *bitsArray, uint32_t bit){
if(TestBitSet(bitsArray, bit)){
ClrBit(bitsArray, bit);
}else{
SetBit(bitsArray, bit);
}
}现在,我一直在思考这一实施的弱点。首先,有三个输入的OR门的实现是对具有两个输入的OR门的某种代码重复。更多输入的OR门也是如此。第二个问题是门是如何“沟通”和形成整个逻辑的。这里我一直在使用位数组。我还考虑到了每个逻辑块接收到协作逻辑块的指针的可能性。我之所以不使用这种方法,是因为我认为这种实现会导致树形数据结构,在对整个逻辑进行评估的情况下,堆栈可以快速增长,特别是在复杂的逻辑结构中。
有没有人知道如何解决我的问题,即代码重复和逻辑块之间的通信?谢谢你的建议。
发布于 2019-07-22 17:47:30
首先,我尝试将从电压级别到逻辑级别的映射合并到一个地方。例如:
class positive_signal {
unsigned value : 1;
public:
bool isSet() const { return value == 1; }
void set() { value = 1; }
void clear() { value = 0; }
};
class negative_signal {
unsigned value : 1;
public:
bool isSet() const { return value == 0; }
void set() { value = 0; }
void clear() { value = 1; }
};请注意,我在这里遗漏了很多细节,只是给出了一个总体方向的草图。例如,在实践中,您很有可能希望set和clear成员函数能够支持return *this;。
我会考虑实现每种门类型,以获得任意数量的输入:
namespace logic {
bool OR(std::vector const &inputs) {
return std::any_of(inputs().begin(), inputs.end(),
[](signal in) { return in.isSet; });
}
bool AND(std::vector const &inputs) {
return std::all_of(inputs.begin(), inputs.end(),
[](signal in) { return in.isSet; });
}
// and so on
}这避免了具有不同输入数的门的重复逻辑,因为对于这两种输入都使用完全相同的代码。
不过,把这两者结合起来可能有点棘手。正如我上面展示的代码,它们不太适合。你可以走几条不同的路线。一种是使用继承,因此您可以从signal作为一个抽象基类开始,然后从它派生出正负信号。如果这样做,就必须将指针的向量传递给信号,而不是信号的向量。
或者,您可以将信号类型作为模板参数传递给门,这样编译器将实例化一个负逻辑的OR,为正逻辑实例化一个OR (以此类推)。
在这种情况下,我认为后者更合适。如果您期望创建一个类似于5输入或门的东西,其中2个任意输入是负逻辑,而另三个正逻辑(或者更一般地说,输入可以是任何正负逻辑的任意组合),继承就会有意义。然而,在现实中,您通常在门级定义逻辑类型,因此一个5输入OR门将把所有5个输入作为正逻辑,或者将所有5个输入作为负逻辑,但绝不将两者结合在一起。
发布于 2019-07-22 16:57:05
LogicBlocks::Or_02::Or_02(uint32_t *bitsArray,
uint32_t input_01, LogicType_e inputType_01,
uint32_t input_02, LogicType_e inputType_02,
uint32_t output):
m_BitsArray{bitsArray},
m_In01{input_01}, m_In01Type{inputType_01},
m_In02{input_02}, m_In02Type{inputType_02},
m_Out{output}{
}成员变量是按照类中指定的顺序初始化的,而不是初始化程序列表中的顺序(因此m_BitsArray将被最后初始化)。虽然这里没有什么会中断,但最好始终在初始化程序列表中使用正确的顺序。
(Utils::TestBitSet(m_BitsArray, m_In01) && m_In01Type == POS)有很多这样的代码。为什么不将其考虑到一个函数中,同时考虑所有三个变量:
Utils::TestBitSet(m_BitsArray, m_In01, m_In01Type);我不太喜欢分开的TestBitSet()和TestBitClr()。只要有一个IsBitSet()并在适当的地方使用!IsBitSet()就更整洁了。
new LogicBlocks::Or_02不要只漏记忆。使用std::unique_ptr,或者手动删除它。
LogicBlk必须有一个虚拟析构函数(然后也不需要在派生类中指定空析构函数)。
virtual Update(void);
void参数在C++中是不必要的。
#define LSig01 (LW_01*32 + 0x00)
...与#defines相比,更喜欢常量静态变量,因为它们有适当的作用范围。
Or_03(uint32_t* const bitsArray,
const uint32_t input_01, const LogicType_e inputType_01, ...不要制作由值const传递的函数参数。这些consts对打电话的人来说并不重要,他们隐藏了那些确实重要的consts (例如& vs const&或* vs const*),这使得声明很难读懂。C++还允许在声明中使用const与函数定义中使用const不同,因此甚至可能会产生误导。
我们可以对不同数量的输入使用模板:
struct LogicInput {
LogicType_e type;
std::uint32_t value;
};
template
class Or {
public:
Or(uint32_t* bitsArray, std::array, NumInputs> in, uint32_t out);
...
std::array m_In;
std::uint32_t m_Out;
uint32_t* m_BitsArray;
};https://codereview.stackexchange.com/questions/224676
复制相似问题