是否可以创建一个函数而不是两个具有相同目的但参数类型不同的函数? (我可以删除重复的代码吗?)[重复]

Posted

技术标签:

【中文标题】是否可以创建一个函数而不是两个具有相同目的但参数类型不同的函数? (我可以删除重复的代码吗?)[重复]【英文标题】:Is it possible to create one function instead two functions of same purpose but different argument type? (Can I remove duplicate code?) [duplicate] 【发布时间】:2015-04-08 05:03:11 【问题描述】:

是否可以创建一个函数而不是两个具有相同目的但参数类型不同的函数?

我有两个用 C 编写的函数,它们将图像从 RGB 转换为 HSV:

void png_rgb2hsv(pPNG_DATA data);
void jpg_rgb2hsv(pJPEG_DATA data);

他们做同样的事情:他们接受 data->row_pointers 并在循环 data->height 次中循环它。然后它转换 data->row_pointers 引用的值。这就是它的工作原理。但唯一的区别是数据结构使用不同的类型。在我看来,为同一件事使用两个功能是没有意义的。尤其是当我要为更多色彩空间添加更多功能时。

这个程序设计问题在 C 中是如何在实践中解决的?

更新: 大多数读者不明白这个问题。我没有问过超载。这是关于设计的问题。我问是否可以删除冗余功能,“冗余”代码。因为两个函数使用相同的代码,所以做同样的事情,但是函数参数中的类型不同,因为一种类型来自 libjpeg,第二种来自 libpng。我发现这是不可能的,因为这意味着将一个变量用于两种不同的类型

【问题讨论】:

根据你的描述,为什么这里有两种数据类型?如果它们都是像素数据的容器,它们应该是一样的吗?或者有一个相同的“子类型”可以通过? jpeg 和 png 等图像有两个不同的库:libjpeg 和 libpng,它们使用不同的类型。 它们有不同的类型,但您可以完全以相同的方式使用它们吗?相同的语法、成员名称等? Void 指针与指向任何对象的指针兼容。您可以使用void* 并添加“类型”参数:void data2hsv(void *data, int datatype) if (datatype == 0) /* use png */; else /* use jpg */; 是的。相同的成员名称。我可以以完全相同的方式使用它们。 【参考方案1】:

使用通用宏:

void png_rgb2hsv(pPNG_DATA data);
void jpg_rgb2hsv(pJPEG_DATA data);

#define rgb2hsv(X) _Generic((X), pPNG_DATA: png_rgb2hsv, pJPEG_DATA: jpg_rgb2hsv)(X)

(如果你的编译器太旧并且不支持这个技巧(或者你因为某种原因不想使用它),那么看起来没有办法让两个函数具有相同的名称和不同的参数类型。然后,您需要选择其他解决方案。)

更新:

我误解了 OP 的问题。 如果你想为这两种类型创建一个 single 函数,你应该这样做:

void rgb2hsv_(Something *row_pointers, int height) // any member that you need goes here

    /* your code */


#define rgb2hsv(obj) \
do  \
_Generic((obj), pPNG_DATA: pPNG_DATA, pJPEG_DATA: pJPEG_DATA) tmp = (obj); \
    rgb2hsv_(tmp->row_pointers, tmp->height /*again, every member that you need should be stated here*/) \
 while (0)

另外,如果pJPEG_DATApPNG_DATA 具有完全相同的内部布局(这意味着它们的成员具有相同的类型并且它们以相同的顺序列出),你可以试试这个:(它不安全和上一个一样,但至少它看起来不像是一个糟糕的黑客)

void rgb2hsv(void *ptr)

    pPNG_DATA data = (pPNG_DATA *) ptr; // it does not matter which of 2 types you use here
    /* put your code that uses `data` here */

但请记住,如果您在这些结构中的任何一个中交换 2 个成员或以任何方式更改它们的内部结构,那么这件事可能不再起作用。

(另外,你应该知道:这两种技术只是获得预期结果的棘手变通方法。最好的方法是将每个需要的成员作为单独的参数传递,不要做这个宏功夫)强>

【讨论】:

不是每个人都可以使用 C11 编译器 (+1 BTW)。 @pmg 哎呀。我以前从未使用过泛型。看起来我的代码(以及来自 cppreference.com 的示例)是错误的,甚至 gcc 不支持泛型。 @user1141649 您应该为 png 和 jpg 创建单独的函数,然后使用 #define rgb2hsv(X) ... 在我的示例中创建一个宏,该宏将根据参数类型调用这些函数之一。 @user1141649 仔细查看最后一行。它定义了一个宏rgb2hsv。您可以将其用作函数:编译器会将rgb2hsv(something); 替换为png_rgb2hsv(something);jpg_rgb2hsv(something);,具体取决于something 的类型。 @HolyBlackCat gcc 和 clang 都支持泛型;您可以通过单击运行此代码on cppreference 来查看实际情况【参考方案2】:

主要有两种不同的方法来处理这个问题。您创建一个包含图像的结构以及数据的标签,如下所示:

struct img
    char *filetype;
    void *data;
;

然后你创建一个函数来检查文件类型并调用你想要的任何函数。

或者你创建一个带有函数指针的结构体,指向你想要使用的任何函数,如下所示:

struct imgfuns
    void (*rgb2hsv)(void *);
;

struct imgfuns *init_struct(void)

    struct imgfuns *funs = malloc(sizeof(*funs));
    if(funs == NULL)
        return NULL;
    if(png...)
        funs->rgb2hsv = png_rgb2hsv;
    else if(jpg...)
        funs->rgb2hsv = jpg_rgb2hsv;
    return funs;


int main(int argc, char *argv[])

    struct imgfuns *funs;
    funs = init_struct();
    if(funs == NULL)
      exit(1);
    /* get data from somewhere */
    funs->rgb2hsv(data);
    free(funs);
    return 0;

但是,如果您需要使用许多不同的函数,因为您需要为每个函数映射它们,这可能会很痛苦。但是您将获得更简洁的代码,并且您可以在一个地方处理所有格式,而不是为每种文件格式创建包装函数。

更多信息可以在这里找到:How do function pointers in C work?

【讨论】:

【参考方案3】:

Void 指针与指向任何对象的指针兼容。您可以使用void* 并添加“类型”参数。

void data2hsv(void *data, int datatype) 
    if (datatype == 0) 
        /* use png */
     else 
        /* use jpg */
    

编辑:对编译器撒谎

void data2hsv(void *data) 
    pPNG_DATA source = data; // if data is of pJPG_DATA type 
                             // compiler will not catch the error
    /* use source as if it was pPNG_DATA */

【讨论】:

void rgb2hsv(void *data, int datatype) int c = data->channels; 警告:取消引用“void *”指针错误:请求成员“通道”不是结构或联合......你如何在那里获得所需的类型? 您必须将void* 转换为指向正确数据类型的指针:pPNG_DATA pPNG = data; int c = pPNG->channels; 如果您可以对编译器撒谎,您可以只使用 void 指针并在函数内部转换为 PNG 或 JPG。请记住:如果你对编译器撒谎,它迟早会报复 我可以这样做吗? void rgb2hsv(void *data, enum EXT datatype) if (datatype==PNG) pPNG_DATA data=data; 因为我得到了错误:expected expression before 'pPNG_DATA' ...我正在尝试将数据用于 pPNG_DATA 用于 pJPEG_DATA。我需要两种类型的名称,而不是两种类型的两种名称。 是的,你可以;但是你不能使用data 有两个含义。 查看我的编辑【参考方案4】:

这在 C 中是不可能的,但在许多其他编程语言中,例如 C++,这是可能的。

但是,在 C 语言中存在一些解决该限制的技巧,请参阅this question 了解更多信息。

C 中的一种常见做法是将类型放在函数名称中,如您的示例所示。

【讨论】:

模板不是解决方案的唯一原因是因为 C 没有模板吗? @Celeritas 是的,C 也没有。 唯一正确的答案。谢谢。【参考方案5】:

以下使用一个附加参数来告知函数传递的是什么类型的图像数据:

void rgb2hsv(void *data, int data_type)

    char *row, *pix;
    switch (data_type) 
        case JPG: row= (pJPEG_DATA data)->row; break;
        case PNG: row= (pPNG_DATA  data)->row; break;
    
    ....

【讨论】:

我必须为我需要合作的每个成员都这样做吗?不能直接将数据设置为(pJPEG_DATA数据)或(pPNG_DATA数据)? 是的,这也是可能的。只需使用示例,因为您将成为一名程序员 但我不使用 char* 类型。我在结构中使用这些类型:png_bytep * row_pointers; png_uint_32 height; png_uint_32 channels; 对于 jpeg,会有类似的东西:jpg_bytep * row_pointers; jpg_uint_32 height; jpg_uint_32 channels;。所以我不能使用你的解决方案,因为你希望我使用相同的类型,但它们是不同的类型。它们的使用方式相同,但它们来自不同的库 如果您检查这些类型,它们将归结为相同的基本类型。 png_bytep 和 jpg_bytep 似乎都是指向字节的指针,所以是相同的基本类型。如果你想避免多个函数,整个技巧就是统一类型,这样你就可以在一个函数或循环中处理它们。【参考方案6】:

您不能在 C 中重载函数,但您可以组织代码以使其达到最佳状态。我将为rgb2hsv 创建一个函数,它将获得void * 类型的参数,确定类型并使用它,处理所有可能的情况。你应该只使用这个功能。在较低级别上,您仍然可以复制您的函数,在较高级别上,您不必调用两个单独的函数。这将简化使用。

【讨论】:

您会使用一个变量名称来执行所有计算,还是使用两个名称?如果是第二个,那么您在这里的大多数人都不理解我的问题。 我会为这些操作使用一个名称。那将是一个代理函数,它处理类型并在它是 png 时调用 png_rgb2hsv,如果它是 jpg 则调用 jpg_rgb2hsv。实际上,您将不得不使用函数,但不是一直调用它们,您将拥有一个代理函数来处理函数选择。您也可以将其实现为单个函数,但从管理的角度来看并不理想。 很遗憾,所有读者都误解了这个问题。我问如何不重复代码或如何删除重复的函数...看起来这是不可能的,因为不可能将一个变量名用于两个不同的类型。

以上是关于是否可以创建一个函数而不是两个具有相同目的但参数类型不同的函数? (我可以删除重复的代码吗?)[重复]的主要内容,如果未能解决你的问题,请参考以下文章

两个表作为用户定义函数的参数

在Java中,是不是可以有两个具有完全相同签名的函数,除了一个是静态的

比较/区分两个“相同的”CLBeacon

Python __init__ 和 classmethod,它们是不是必须具有相同数量的参数?

检查两个 Python 函数是不是相等

当两个产品具有相同的升级代码时升级一个产品而不是另一个