【问题标题】:How to make an output function similar to Python's in C?如何在 C 中制作类似于 Python 的输出函数?
【发布时间】:2020-09-24 10:00:22
【问题描述】:

我正在开发一种名为 BPML 的自定义编程语言,我想尝试更新名为 say() 的输出函数。

C 中的最新版本:

void say(char *text) {
    printf("%s", text);
}

Python 的最新版本:

def say(text):
    print(text, end = '')

在 C 中,我希望它像 Python 的 print() 函数一样,只需在函数中输入变量即可,但在 C 中并非如此,因为您仍然必须使用适当类型的变量。

这就是我制作的程序出错的原因:

#include <stdio.h>

void say(char *text);

int main() {
    int number = 21;
    say(number);
}

void say(char *text) {
    // Output function
}

错误:

P1.c:7:9: error: incompatible integer to pointer conversion passing 'int' to parameter of type 'char *' [-Werror,-Wint-conversion]
    say(number);
        ^~~~~~

我仍然需要使用&lt;stdlib.h&gt; 中的itoa() 函数,我没有想到,因为我使用的C 版本中没有它。

那么有没有可能做出某种类似于 Python 函数作用方式的输出函数呢?

【问题讨论】:

  • 也许我们必须看看 Python 是如何实现这一点的,这可能是通过运行时类型检测(速度慢,但允许这样的泛化函数)
  • 听起来你想要一个多态函数。 C没有这些。相反,您可以创建自己的通用数据类型包装器,其中包括类型信息,然后将它们的实例传递给 say
  • 您是在尝试用 C 还是您的自定义 BPML 语言编写函数?听起来您真正拥有的是语言设计问题,您需要弄清楚您的语言设计将如何处理这个问题。也许你需要你的语言有函数重载,或者动态类型等等。
  • @user2357112supportsMonica 我还没有开始制作编译器、语言文件和其他东西。我可能需要这方面的帮助。我只是从基础开始,先用不同的语言制作函数。
  • @BPML:不过,您接受的_Generic 答案对于您计划编写的语言几乎肯定会 100% 无用。它非常特定于 C。

标签: python c output


【解决方案1】:

C 没有重载,也没有函数模板。但是你可以在C11及以上的宏和泛型选择中选择合适的格式:

#include <stdio.h>

#define say(X) printf(_Generic((X),    \
                        double: "%f ", \
                        float:  "%f ", \
                        char *: "%s ", \
                        int:    "%d "  \
                ), (X));

int main(void) {
    say(21);
    say(21.5);
    say(21.5f);
    say("Hello world");
}

这可以与 X-macros 结合使用,以轻松拥有真正强大的构造:

#include <stdio.h>

// you can easily add new supported types here
#define SAY_FORMATS(X) \
    X(double, "%f")    \
    X(float,  "%f")    \
    X(char *, "%s")    \
    X(int,    "%d")
    
// add space after each item. Leading comma so that we do not
// need to have a dummy entry in the end. Unfortunately C 
// does not like trailing commas in _Generic. Thanks to 
// user694733 for the idea
#define GENERIC_ENTRY(Type, Format) \
    , Type: Format " "

#define say(X)                  \
     printf(_Generic((X)        \
         FORMATS(GENERIC_ENTRY) \
     ), (X))

int main(void) {
    say(21);
    say(21.5);
    say(21.5f);
    say("Hello world");
}

【讨论】:

  • FWIW,here 是使用 _Generic 进行打印的扩展。
【解决方案2】:

无论您是在 C 中执行此操作,还是在 C 中实现的其他语言,您都可能需要使用 C 原语以外的东西来存储您的数据。原则上,union 可以存储多种数据类型,但它可能不够灵活,无法满足您的需求。

编译器实现者通常使用这种通用形式的结构来存储编译时未知类型的数据:

struct GenericData
  {
  int i;
  char *str;
  double d;
  // ... other types
  enum Type t;
  };

这里,成员t 表示在任何给定时间,各个字段中的哪些字段包含有效数据——无论是表示整数、字符串、浮点数还是其他。所以你可以像这样定义你的say 函数:

void say (const struct GenericData *data)
  {
  ...
  }

你会像这样使用它:

GenericData *v = // some function to intialize it
say (v);

当然,您需要函数来使用正确类型的数据初始化您的值、对它们进行数学运算、在使用后整理它们等等。

例如,GLib 库(由 GTK 和 Gnome 使用)内置了对“变体”数据类型的支持,使用名为 GVariant 的结构:

https://developer.gnome.org/glib/stable/glib-GVariant.html

我想任何曾经编写过编译器或解释器的人都会经历过处理变体类型的过程,并且会知道涉及到多少工作。不要低估工作量或难度。

【讨论】:

    【解决方案3】:

    这是不可能的。 C does not support type checking in runtime.

    这里是 Python 的 print() 的来源,尽管我怀疑它是否会有很大帮助: link

    看看它所采用的参数——而不是单个原语。

    static PyObject *
    builtin_print(PyObject *self, PyObject *args, PyObject *kwds)
    

    【讨论】:

    • 对于 C,类型检查不是运行时问题,而是编译时问题。
    【解决方案4】:

    我决定将Antti Haapala's answer 扩展为类似于printf() 函数中的每一种格式:

    #define say(X) printf(_Generic((X),                     \
                          double: "%f", float: "%f",        \
                          char *: "%s", char: "%c",         \
                             int: "%d", unsigned int: "%u", \
                      signed int: "%i", void *: "%p"
                   ), (X));
    

    我不知道如何采用%e%g%x%o%a 的格式,因为它们都采用浮点值(floatdouble) 或unsigned int

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-13
      • 2017-03-15
      • 2018-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-14
      相关资源
      最近更新 更多