如何设计一个C ++程序以允许在运行时导入功能?


10

今天,我想问您一个有关C ++实现特定软件体系结构功能的问题。

当然,我使用了搜索但是没有找到任何直接链接的答案。

基本上,我的目标是建立一个程序,使用户可以对任意组成的物理系统(例如驾驶汽车)进行建模和仿真。我假设有一个物理模型库(类中的函数)。每个函数可以具有一些输入,并根据基础的物理描述返回一些输出,例如,内燃机模型,空气阻力模型,车轮模型等。

现在,该想法是为用户提供一个框架,该框架允许他根据自己的需求来组合任何功能,即映射任何身体行为。该框架应提供功能,以连接不同功能的输出和输入。因此,该框架提供了一个容器类。我称它为COMPONENT,它可以容纳一个或多个模型对象(FUNCTION)。这些容器还可以容纳其他组件(参见复合模式)以及功能参数之间的连接(CONNECTOR)。此外,组件类提供了一些通用的数字功能,例如数学求解器等。

功能的组合应在运行时完成。在第一个实例中,用户应该能够通过导入定义合成结构的XML来建立合成。后来,人们可能会想到添加GUI。

为了使您更好地理解,这里是一个非常简化的示例:

<COMPONENT name="Main">
  <COMPONENT name="A">
    <FUNCTION name="A1" path="lib/functionA1" />
  </COMPONENT>
  <COMPONENT name="B">
    <FUNCTION name="B1" path="lib/functionB1" />
    <FUNCTION name="B2" path="lib/functionB2" />
  </COMPONENT>
  <CONNECTIONS>
    <CONNECTOR source="A1" target="B1" />
    <CONNECTOR source="B1" target="B2" />
  </CONNECTIONS>        
</COMPONENT>

不必要深入研究框架的功能,因为我的问题更为普遍。编译框架代码/程序时,物理问题描述以及用户定义的功能未知。当用户选择功能(通过XML或以后通过GUI)时,框架应读取功能信息,即应获取输入和输出参数的信息,以便为用户提供互连功能的选项。

我知道反射的原理,并且我知道C ++不提供此功能。但是,我确信经常需要“在运行时构建对象”的概念。我应该如何在C ++中设置软件体系结构以实现我的目标?C ++是正确的语言吗?我忽略了什么?

提前致谢!

干杯,奥利弗


C ++确实具有函数指针和函数对象。所有功能都已编译到可执行文件中,还是在动态库中(在哪个平台上)?
卡雷斯(Caleth)'17

1
从通常需要电气工程/ [电子设计自动化(EDA)](en.wikipedia.org/wiki/Electronic_design_automation)或机械工程/ 计算机辅助设计(CAD)的大学学位的意义上讲,这个问题太过广泛。相对而言,调用C / C ++动态库非常容易,请参阅x86的C调用约定。但是,可能需要操纵堆栈(通过CPU堆栈指针)和CPU寄存器值。
rwong

1
C ++语言不支持动态加载函数。您将必须查看特定于平台的内容。例如,Windows上的C ++编译器应支持Windows DLL,后者确实支持某种形式的反射。
西蒙B

在C ++中,实际上很难调用在签名时不知道其签名(参数和返回类型)的函数。为此,您需要了解函数调用在所选平台的组装级别如何工作。
Bart van Ingen Schenau '17

2
我要解决的方法是编译c ++代码,为支持eval命令的任何语言创建解释器。使用C ++解决了爆炸问题。:P请考虑一下为什么这还不够,请更新问题。当明确实际需求时,它会有所帮助。
candied_orange

Answers:


13

在纯标准C ++中,您不能“允许在运行时导入功能”。根据该标准,C ++函数集在构建时(实际上是链接时)是静态已知的,因为它是由构成程序的所有翻译单元的并集固定的。

实际上,大多数情况下(不包括嵌入式系统),C ++程序在某些操作系统上运行。阅读操作系统:三篇简单文章,以获得良好的概述。

一些现代操作系统允许动态加载插件。POSIX特别指定dlopendlsym。Windows有所不同LoadLibrary(链接模型较低;您需要显式注释相关功能,插件提供或使用的功能)。顺便说一句,在Linux上,您实际上可以dlopen使用很多插件(请参阅我的manydl.c程序,足够耐心,它可以生成然后加载近一百万个插件)。因此,您的XML东西可能会推动插件的加载。您的多组件/多连接器描述使我想起了Qt信号和插槽(这需要moc预处理器;您可能也需要类似的东西)。

大多数C ++实现都使用名称修饰。因此,您最好将其声明为extern "C"与插件相关的功能(并在其中定义并dlsym从主程序访问)。阅读C ++ dlopen mini HowTo(至少适用于Linux)。

BTW,QtPOCO是C ++框架,为插件提供了一些可移植的高级方法。使用libffi,您可以调用仅在运行时才知道其签名的函数。

另一种可能性是在程序中嵌入一些解​​释器,例如LuaGuile(或像Emacs一样编写自己的解释器)。这是一个强有力的建筑设计决策。您可能需要阅读Lisp in Small PiecesProgramming Language Pragmatics中的更多内容。

这些方法有多种变体或混合体。您可以使用一些JIT编译库(例如libgccjitasmjit)。您可以在运行时在临时文件中生成一些C和C ++代码,将其编译为临时插件,然后动态加载该插件(我在GCC MELT中使用了这种方法)。

在所有这些方法中,内存管理都是一个重要的问题(它是“整个程序”属性,而程序的“信封”实际上是“更改”)。您至少需要一些有关垃圾收集的文化。阅读GC手册中的术语。在许多情况下(任意循环引用中的弱指针是不可预测的),C ++ 智能指针所珍视的引用计数方案可能还不够。另请参见

阅读有关动态软件更新的信息

请注意,某些编程语言(尤其是Common Lisp(和Smalltalk))对运行时导入函数的想法更为友好。SBCL是Common Lisp的一个免费软件实现,可以在每次REPL交互时编译为机器代码(甚至能够进行垃圾回收机器代码,并且可以保存整个核心映像文件,以后可以轻松地重新启动)。


3

显然,您正在尝试使用自己的Simulink或LabVIEW类型的软件,但是使用了恶意的XML组件。

最基本的是,您正在寻找面向图形的数据结构。您的物理模型由节点(您称它们为组件)和边(命名中的连接器)组成。

没有语言强制机制来执行此操作,即使使用反射也是如此,因此您必须创建一个API,并且要播放的任何组件都必须实现多个功能并遵守API规定的规则。

每个组件都需要实现一组功能来完成以下工作:

  • 获取组件的名称或有关它的其他详细信息
  • 获取组件公开的输入或输出数量
  • 询问有关输出的特定输入的组件
  • 将输入和输出连接在一起
  • 和别的

这只是用于设置图形。您将需要定义其他功能来组织实际执行模型的方式。每个功能都有一个特定的名称,所有组件都必须具有这些功能。组件特有的任何内容都必须通过该API才能到达,组件之间的方式相同。

您的程序不应尝试调用这些“用户定义的函数”。相反,它应该在每个组件上调用通用的“计算”函数或类似的函数,并且组件本身负责调用该函数,并将其输入转换为输出。输入和输出连接是该函数的抽象,这是程序应该看到的唯一内容。

简而言之,几乎没有什么是C ++特有的,但是您将必须实现一种针对特定问题域的运行时类型信息。使用API​​定义的每个函数,您将知道在运行时要调用的函数名称,并且知道每个调用的数据类型,并且只需使用常规的旧动态库加载就可以完成它。这将附带大量样板,但这只是生活的一部分。

不过,您要牢记的一个C ++特定方面是最好让您的API成为C API,这样,如果用户提供自己的模块,则可以对不同的模块使用不同的编译器。

DirectShow是一个API,可以完成我描述的所有事情,并且可以作为一个很好的例子。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.