覆盖库中的#define

时间:2013-01-19 21:14:00

标签: c arduino ide c-preprocessor

我为设备创建了一个Arduino库,通常可以通过多种方式配置它。例如。使用中断或轮询它。在其他示例中,我创建了以下文件:foo.h,fooConfig.h和foo.cpp,如下所示。其中fooConfig.h包含如何使用屏蔽的配置。如有或没有中断等...

这样做我希望主草图的INO文件能够覆盖使用#define声明的默认设置。包括在图书馆的实例中。结果表明它确实如此。至少就是我这样做的方式。

以下代码是一个带有问题的简化示例:

definetest.ino

#define BAR USE_POLL
#include <foo.h>

foo test;

void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.print(F("setup's defined BAR was "));
  Serial.println(BAR);

  Serial.print(F("inside foo.begin defined BAR was "));
  Serial.println(test.begin());
}

void loop() {
}

foo.h中

#ifndef FOO_h
#define FOO_h

#include "FOOConfig.h"

class foo {
  public:
    int begin();
};
#endif // FOO_h

FooConfig.h

 #ifndef FOOCONFIG_h
  #define FOOCONFIG_h

  #define USE_INT      1
  #define USE_POLL     2

  #ifndef BAR
    //default to using interrupts
    #define BAR USE_INT
  #endif // BAR

#endif  // FOOCONFIG_h

Foo.cpp中

#include <foo.h>

int foo::begin() {
#if defined(BAR) && BAR == USE_INT
  Timer1.attachInterrupt( isr );  // error here, because of the define...
  return 1;
#elif defined(BAR) && BAR == USE_POLL
  return 2;
#endif
  return 0;
}

产生以下串行输出:

setup's defined BAR was 2
inside foo.begin defined BAR was 1

希望将foo.begin中的BAR等于2,而不是1.注意,希望预编译器决定是否省略attachInterrupt。如果不使用库,请不要依赖和消耗库的资源。只是希望它是一个高级选项。

我理解这可能最好用make文件或eclipse处理,但我试图为目前在1.0.3的Arduino IDE发布这个库。

感谢任何帮助。

仅供参考,真实和完整的代码在这里。 https://github.com/mpflaga/Sparkfun-MP3-Player-Shield-Arduino-Library/tree/master/SFEMP3Shield

4 个答案:

答案 0 :(得分:8)

无论工具链如何,程序都不会也不应该确定如何编译库。库的一个关键概念是它完全开发,编译和打包而不存在任何程序。有一种单向依赖:程序依赖于库 - 库不依赖于任何程序。强调这一点的一个关键点是您不必分发源代码来使用库。一个库可以只使用头文件和二进制文件(.a)查看所有使用的WinAvr库。

您正在寻求的是如何对库进行多种配置。在其他工具链中,这很容易在项目级别完成,您可以在其中创建任意数量的输出配置。在您的情况下,您将使用poll和int,并且工具链将生成两个库,例如:
  libMySomething_int.a
  libMySomething_poll.a

程序选择他们想要使用的库的配置。

正如其他人所说,Arduino IDE不会提供这种复杂性。既然你说你打算留在那个工具链中,这些是我能看到的解决方案:

  1. 制作两个与#define不同的库的副本。这基本上是您提供两种配置。因为它们之间的差别很小,所以WinMerge很容易在两个之间并保持代码库同步。

  2. 重新思考是否有必要为这两个表单设置一个接口。你不必只有一个begin(),你可以有两种形式:

    void beginInt();
    void beginPoll();

    void getSomethingInt();
    void getSomethingPoll();

  3. 请记住,这不会浪费代码空间。链接器将从最终程序中删除所有未使用的函数,因此如果您只使用Int表单,则所有Poll函数都将被删除。

    这两者之间的选择并不明确,有些主观。决定应考虑您打算在库中封装/隐藏的内容,以及您想要明确的内容。如果这个库是各种实时时钟芯片的接口,那么显然可以公平地封装所有差异并提供一组功能。但在你的情况下,你暗示资源消耗,时间和并发性的关键变化 - 我不确定最好隐藏它。

答案 1 :(得分:2)

Arduino IDE单独编译库,因此您需要在编译期间将define作为-D选项传递。 Arduino IDE目前不提供便于您轻松完成的工具。 Arduino 1.5通过boards.txt系统提供了一些功能,但这可能无法提供您所需的灵活性。

因此,正如您所说,选项是在Eclipse等产品中编辑make文件,或者在Visual Studio Pro中设置“Defines”项目属性之一(例如“Defines - Project”)。

或者使用TeensyDuino IDE并将自己的menu. defs添加到boards.txt

答案 2 :(得分:1)

如果您的图书馆仅在主草图中使用 并且您可以将所有代码放在.h文件中(即没有.cpp文件) 那么你就可以达到你想要的效果。

这样做是有问题的编码实践,但在Arduino世界中似乎并不罕见。

PS:完全正确,实际上也可以有一个.cpp文件,但前提是它的编译完全不受可覆盖的#define的影响。

答案 3 :(得分:0)

我不是Arduino专家,但乍看之下。你的foo :: begin()输出1,因为你没有在foo.cpp中定义“BAR”,所以预处理器会降到你的默认值。也许你可以在“foo”类中添加一个“配置”方法。此方法将接收具有所需配置的参数,因此您可以将define as参数从INO文件传递到“foo”类实例。