【问题标题】:Correct way to share global variables between the files在文件之间共享全局变量的正确方法
【发布时间】:2021-06-23 06:51:26
【问题描述】:

我有一个围绕 neopixel led 库的包装器。我已经命名了文件 leds.cpp 和 leds.h

leds.cpp:

#include "leds.h"

RgbColor nice_purple(153,153,255);
RgbColor orange(255,165,0);
RgbColor bright_purple(204,0,102);
RgbColor cyan(0, colorSaturation, colorSaturation);
RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor yellow(colorSaturation,colorSaturation,0);
RgbColor white(colorSaturation);
RgbColor black(0);
RgbColor purple(colorSaturation, 0, colorSaturation);
NeoPixelBus<NeoGrbFeature, NeoEsp32Rmt0800KbpsMethod> strip(PixelCount, PixelPin);

void toggle_led_strip(RgbColor colour,int number){
      for(int i=0;i<=number;i++){
        strip.SetPixelColor(0, colour);
        strip.SetPixelColor(1, colour);
        strip.SetPixelColor(2, colour);
        strip.SetPixelColor(3, colour);
        strip.SetPixelColor(4, colour);
        strip.SetPixelColor(5, colour);
        strip.SetPixelColor(6, colour);
        strip.SetPixelColor(7, colour);
        strip.Show();
        delay(500);
        strip.SetPixelColor(0, black);
        strip.SetPixelColor(1, black);
        strip.SetPixelColor(2, black);
        strip.SetPixelColor(3, black);
        strip.SetPixelColor(4, black);
        strip.SetPixelColor(5, black);
        strip.SetPixelColor(6, black);
        strip.SetPixelColor(7, black);
        strip.Show();
        delay(500);
      }
    }


void toggle_led_strip_on(RgbColor colour){
        strip.SetPixelColor(0, colour);
        strip.SetPixelColor(1, colour);
        strip.SetPixelColor(2, colour);
        strip.SetPixelColor(3, colour);
        strip.SetPixelColor(4, colour);
        strip.SetPixelColor(5, colour);
        strip.SetPixelColor(6, colour);
        strip.SetPixelColor(7, colour);
        strip.Show();   
      }


      void toggle_led_strip_off(){
        strip.SetPixelColor(0, black);
        strip.SetPixelColor(1, black);
        strip.SetPixelColor(2, black);
        strip.SetPixelColor(3, black);
        strip.SetPixelColor(4, black);
        strip.SetPixelColor(5, black);
        strip.SetPixelColor(6, black);
        strip.SetPixelColor(7, black);
        strip.Show();     
      }


void clear_LED_strip(int number){
  for (int i=0;i<number;i++)
         strip.SetPixelColor(i, black);
        //delay(1);
          strip.Show();
}

void LED_strip_ON(RgbColor colour,int number){
    for (int i=0;i<number;i++)
        strip.SetPixelColor(i, colour);
        strip.Show();

}

和 leds.h:


#ifndef LEDS_H
#define LEDS_H

#include "NeoPixelBus.h"
#include <Adafruit_I2CDevice.h>
#define PixelCount  8 // this example assumes 4 pixels, making it smaller will cause a failure
#define PixelPin  27  // make sure to set this to the correct pin, ignored for Esp8266
#define colorSaturation 250


void toggle_led_strip(RgbColor colour,int number);
void toggle_led_strip_on(RgbColor colour);
void toggle_led_strip_off();
void clear_LED_strip(int number);
void LED_strip_ON(RgbColor colour,int number);


#endif

现在我想在多个其他文件中使用这些函数,我想澄清几个问题。

  1. 我有 modbus.cpp 和 modbus.h 文件,我在 modbus.cpp 中编写的功能之一涉及切换 LED 灯条 我需要在这个文件中包含 leds.h 才能使用这些功能。

我是否在我的 modbus.h 或 modbus.cpp 中执行 #include "leds.h"?有什么区别吗?

  1. 所有 LED 功能都以颜色作为参数。颜色变量类型为 RgbColor。所有颜色都在 leds.cpp 中定义。然后我必须将我将在我的 modbus 中使用的颜色声明为 extern,例如 extern RgbColor blue;。再一次,我担心它是否应该在 modbus.h 或 modbus.cpp 中声明。

感谢任何建议。提前致谢。

我的modbus.cpp涉及到的功能有:


#include "modbus_custom.h"



enum MODBUS_REGISTERS{
  MAIN_REG = 10,
  QUANTITY_REG = 11,
  SLAVE_ID_REG = 12,
  SERIAL_LEN_REG = 13,
  SERIAL1_REG = 14,
  SERIAL2_REG = 15,
  SERIAL3_REG = 16,
  SERIAL4_REG = 17,
  SERIAL5_REG = 18,
  SERIAL6_REG = 19,
  SERIAL7_REG = 20,
  SERIAL8_REG = 21,
  SERIAL9_REG = 22,
  SERIAL10_REG = 23,
  NUMBER_TO_PICK_REG = 24,
};



ModbusRTU mb; // class object 

void Read_modbus_Pildymas(){
    switch(mb.Hreg(MAIN_REG)){
      case 1:
        Serial.println("Received start of pildymas code");
        mb.Hreg(MAIN_REG, 0);   //ALWAYS REPLY WITH 0 IN THE REGISTER
        break;
      case 2:
        Serial.println("Received 2 code");
        toggle_led_strip(blue, 2);
        
    }
}

我的 modbus.h 看起来像:

#ifndef MODBUS_CUSTOM_H
#define MODBUS_CUSTOM_H

#include <ModbusRTU.h>
#include "definitions.h"
#include "leds.h"



#define RXD2 16
#define TXD2 17
#define DIR 15
#define REG_Pildymas 10
#define REG_Picking 100


extern uint16_t SLAVE_ID;
extern item_inside_struct item_inside;
extern int pildymas_flag;
extern char current_status[20];
extern int clear_data_flag;
extern int number_to_pick;
extern int screen_colour;
extern char DEVICE_ID[10];

extern RgbColor blue;
extern Adafruit_ST7735 tft;

void Read_modbus_Pildymas();
void modbus_respond_pildymas();
void modbus_respond_clear_data();


#endif

尝试伦丁法

在我的 leds.h 中,我现在创建了一个包含所有可能颜色的枚举:

typedef enum
{
  nice_purple,
  orange,
  bright_purple,
  cyan,
  red,
  green,
  blue,
  yellow,
  white,
  black,
  purple,
} color_t;

在我的 leds.cpp 中:

我已经创建了这个静态的 const RgbColor colors[11]:

static const RgbColor colors[11] =
{
  [nice_purple] = {153,153,255},
  [orange]  = {255,165,0},
  [bright_purple]  = {204,0,102},
  [cyan]  = {0, colorSaturation, colorSaturation},
  [red]  = {colorSaturation, 0, 0},
  [green]  = {0, colorSaturation, 0},
  [blue]  = {0, colorSaturation, 0},
  [yellow]  = {colorSaturation,colorSaturation,0},
  [white]  = {colorSaturation},
  [black]  = {0},
  [purple]  = {colorSaturation, 0, colorSaturation},
};



void toggle_led_strip(color_t colour,int number){
      for(int i=0;i<=number;i++){
        strip.SetPixelColor(0, colour);
        strip.SetPixelColor(1, colour);
        strip.SetPixelColor(2, colour);
        strip.SetPixelColor(3, colour);
        strip.SetPixelColor(4, colour);
        strip.SetPixelColor(5, colour);
        strip.SetPixelColor(6, colour);
        strip.SetPixelColor(7, colour);
        strip.Show();
        delay(100);
        strip.SetPixelColor(0, black);
        strip.SetPixelColor(1, black);
        strip.SetPixelColor(2, black);
        strip.SetPixelColor(3, black);
        strip.SetPixelColor(4, black);
        strip.SetPixelColor(5, black);
        strip.SetPixelColor(6, black);
        strip.SetPixelColor(7, black);
        strip.Show();
        delay(100);
      }
    }




我不完全了解这些数据的结构,请您为我澄清一下吗?据我了解,我创建了一个包含 11 个 RgbColor 元素的数组。

[nice_purple] = {153,153,255},

为什么需要这些大括号? { 153,153,255 } 为什么在这里使用括号? [ nice_purple ]

在我的 main.c 中包含“leds.h”

我调用函数如下:

toggle_led_strip(red,1);

我不完全理解在 leds.h 文件中使用 enum。 我假设我的枚举中的每种颜色都将分配从 0 到最后一个颜色编号的数字。

然后在我的 RgbColor 数组中,我简单地将第 0 个元素声明为 {153,153,255},将第 1 个元素声明为 {255,165,0},依此类推。这种理解正确吗?

【问题讨论】:

  • 主要:尽可能在本地包含文件,因此首先考虑 .cpp 文件。但是,如果 LED 类型、函数或变量是您的 modbus“库”的 public 接口的一部分,那么它应该包含在标题中。 然而: Modbus 独立于任何 LED 的东西,LED 无论如何都是通过一些通信通道控制的。所以宁愿颠倒关系:使用来自LED的modbus,i。 e.在led.cpp 中包含modbus.h。这提高了您的 modbus 实现的可重用性。
  • modbus.h 是公共标头还是仅由 modbus.cpp 使用。如果它不是公共标头,或者如果 led 定义不应暴露给 modbus 组件的用户,则 led.h 应仅包含在 modbus.cpp 中(反之亦然)。
  • 在 C++ 中,我什至将 modbus 抽象出来并隐藏在一个通用接口后面,这样 LED 就可以使用 modbus、以太网、串行或任何其他可用的接口。 也可以用 C 来完成,只是实现起来不太方便,所以你可能不想走那么远......
  • 您能澄清一下公共标头是什么意思吗? modbus.h 和 modbus.cpp 是我为我也编写的 modbus 包装器创建的文件。我有我所有的 modbus_read、modbus_write 函数以及一些额外的 modbus 东西。当接收到某些 modbus 数据时,我想闪烁并发光,因此我需要我的 modbus 文件中的 led 功能
  • @kaylum '反之亦然' – 我建议 only 反之亦然,请参阅我的第一条评论。如果 modbus 知道 LED,就好像 TCP 知道 HTTP...

标签: c++ class header


【解决方案1】:

我是否在我的 modbus.h 或 modbus.cpp 中执行 #include "leds.h"?有什么区别吗?

这个有点主观,有不同的风格。

  • 一种风格是仅在内部使用的标头应包含在 .c/.cpp 文件中。好处是减少了命名空间的混乱,因为包含您的标头的应用程序代码不会同时包含其他标头。
  • 另一种风格是模块使用的所有头文件都应包含在 .h 文件中,作为向应用程序程序员记录该模块使用哪些库的一种方式。这是某个模块中存在的所有程序依赖项(“耦合”)的自记录代码。头文件被视为公共文件,而 .c 文件被视为私有文件,因此应用程序程序员不必挖掘 .c 文件即可找到(它甚至可能不可用)。当有人导入您的模块但忘记链接一些必需的 .c 文件时,此方法还可以更轻松地追踪神秘的链接器错误。

我个人订阅了“始终将所有包含在标题中”阵营。

然后我必须将我将在我的 modbus 中使用的颜色声明为 extern,例如 extern RgbColor blue;。再一次,我担心它是否应该在 modbus.h 或 modbus.cpp 中声明。

这是完全错误的设计。您的 .cpp 文件中的这些颜色定义是该文件的私有(应设为static)。调用者不需要也不应该需要关心他们。问问自己为什么 Modbus 模块需要知道颜色是如何定义的……显然,它不应该。它应该只知道 Modbus 而不是别的。

经验法则:在 C 或 C++ 程序中出现extern 几乎总是表明设计不佳和意大利面条式编程。有一些罕见的例外,但如果您的设计是合理的,通常您永远不需要使用它。

您应该做的是在 LED 标头中为调用者提供一个枚举,例如:

typedef enum
{
  GREEN,
  BLUE,
  ...
} color_t;

然后你的 API 应该使用这个枚举:

void toggle_led_strip_on (color_t color);

然后在 leds.cpp 文件中你应该有类似的东西(这是伪代码,我不太确定你使用的是 C 还是 C++):

static const RgbColor colors[n] =
{
  [GREEN] = {0, colorSaturation, 0},
  [BLUE]  = {0, 0, colorSaturation},
  ...
};

现在您可以使用调用者传递的枚举作为表查找。

【讨论】:

  • @Aconcagua 代码是 C++。因此我的困惑。无论如何,这里的具体语法并不重要,正确的程序设计与语言无关。
  • 哦,对了,还有Serial.println(...),我忽略了。看起来像 Arduino,虽然 Serial 可能是某个结构的变量或常量,其函数指针名为 println,因此 可能 也可以是 C...
  • @Aconcagua C 没有构造函数或模板...
  • 你赢了 ;) – 虽然缺少初始化程序的 mb 仍会编译为 C,但由于未设置函数指针而运行到 UB,但模板是确定的。你的眼睛比我的敏锐,我需要对&lt; 进行文字搜索才能发现;)
  • 呸,我太瞎了 :( 'my modbus.cpp' 在我眼皮底下的文字中。我编辑了问题标签,太明显了。
【解决方案2】:

这个答案不会讨论整体的界面设计(即好不好)。答案基于 OP 已经选择的设计。

我是否在我的 modbus.h 或 modbus.cpp 中执行 #include "leds.h"?

如果 modbus.h 中的任何内容都不需要了解 leds.h 中的内容,则可以在 modbus.cpp 中进行包含。否则,在 modbus.h

有什么不同吗?

当您在 modbus.h 中进行包含时,“modbus”的所有用户也会知道“leds”。如果他们不需要这些信息,则没有理由将其提供给他们。

此外,当您在编译单元中包含不必要的文件时,它可能会减慢编译速度。

然后我必须将我将在我的 modbus 中使用的颜色声明为 extern,例如 extern RgbColor blue;。再一次,我担心它是否应该在 modbus.h 或 modbus.cpp 中声明。

两者都没有。外部声明应该进入 leds.h

【讨论】:

  • 谢谢先生!很有帮助。 Neither. The extern declations should go into leds.h。现在这对我来说突然变得有意义了.. 而不是 extern rgbcolor blue;在我想使用这种颜色的每个源文件中,我可以在我的 leds.h 中执行 extern rgbcolor blue ,当我将它包含在我的其他 .cpp 文件中时,它会自动出现...
  • "当你在 modbus.h 中包含时,"modbus" 的所有用户也会知道"leds"。如果他们不需要这些信息,没有理由将它提供给他们。”虽然这是主观的。虽然他们可能不需要翻译单元的全局命名空间中的这些信息,但应用程序程序员确实需要知道某个模块具有哪些依赖项。
  • @Lundin 考虑到包含所有标头的静态库可能会揭示不必要也不希望了解的内部结构。然后你会写单独的标题来暴露真正的公共接口吗?
  • @Aconcagua 如果这些标头在没有意大利面条式全局变量和正确命名的标识符的情况下正确编写,则不会造成任何伤害。对我个人而言,赢得胜利的论点是,当您在项目中包含一些第 3 部分库时,构建并获得“神秘的链接器错误:1”,您真的想知道哪些 .c 文件丢失并且您应该关联。现在,如果您无权访问包含所在的 .c 文件(因为它们实际上是 lib、dll、obj 或其他任何东西),那么就没有办法了。如果包含在公共标头中,那么您会在几秒钟内找到。
  • @Lundin 好吧,应该知道相应的源文件是如何命名的。但是还缺少更重要的信息:这个源文件编译到了哪个中?在这种情况下,正确记录该库的依赖关系可能更有价值。
猜你喜欢
  • 1970-01-01
  • 2019-08-17
  • 1970-01-01
  • 2015-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-21
  • 1970-01-01
相关资源
最近更新 更多