C api 同时兼容32位和64位整数

C api compatible with 32 and 64 bit integers simultaneously

我有一个 C api 到使用 int 类型的共享(或静态)库,我想升级到 int64_t。 这样做,我想确保我以前的用户仍然可以升级库,而不必重写他们的整个代码。

我已经根据这个要点提出了解决方案,它复制了我的代码行为(使用回调):https://git.io/vMy8G

example.c

// User defines a different type
#define MYLONG int

#include "interface.h"
#include "stdio.h"

// User callback using his own type
int test(const int i, const my_long var, const int j) {
    printf("i   = %d\n",i);
    printf("var = %lld\n",(long long) var);
    printf("j   = %d\n",j);
    printf("%lld\n", 2LL*var-11);
    return var - 7;
}

int main() {
    // This is what user sees
    api_func functionPtr = &test;
    define_callback(functionPtr);

    // Simulate callback call
    call_callback();
    return 0;
}

interface.h

#pragma once

// MYLONG is defined differently internally and externally (before importing this file)
typedef MYLONG my_long;

// Callback definition
typedef int (*api_func)(const int, const my_long, const int); // surround by int to test alignment issues

void define_callback(api_func fptr);
void call_callback();

internal.c

#define MYLONG long long

#include "interface.h"

// Callback handling
api_func callback_ptr = 0;

void define_callback(api_func fptr) {
    callback_ptr = fptr;
}

void call_callback() {
    (*callback_ptr)(1000, 100000, 100);
    (*callback_ptr)(1000, 10000000000, 100); // will overflow when user uses int
}

库将始终使用 #define MYLONG int64_t 编译,而用户将使用 #define MYLONG int64_t#define MYLONG int(这将根据其他一些设置自动完成)。最新的定义将确保向后兼容性。

Valgrind 检查通过所有构建。

我的问题如下:

请注意,如果可能,我想避免编写所有函数的 64 位版本。此外,这必须适用于 Linux (gcc)、Mac (gcc) 和 Windows (Visual Studio)。

要编写最好的移植代码,最好的办法就是不要依赖大小。如果您明确使用 uint32_tint64_t.

否则使用原生 intlong

要求用户为完全不必要的东西定义宏是容易出错的错误。

  1. 安全吗?

没有。 example.c 中的用户将 callback_ptr 设置为:

int test(const int i, const int var, const int j)

... call_callback() 称其为:

int (*api_func)(const int, const int64_t, const int)

每当 intint64_t 的类型不同时,这就是 UB(不是溢出)。

  1. 我是否依赖编译器的任何 non-guaranteed 行为?

是的。

  1. 为什么它会(或不会)起作用? (规范中有关于此的段落吗?)

这是未定义的行为 (UB)。代码正在调用具有一个签名的函数,但该函数可能具有不兼容的签名。

  1. 您认为有更好的方法吗?

是的。
A. 当然不要使用 mis-matched-able 函数签名进行编码。
B. 最后,我认为 OP 需要一种新方法。我建议 添加 函数 define_callback64(),然后让用户像以前一样用 int (*f)(const int, int64_t, const int)define_callback() 调用 define_callback64()call_callback() 然后可以使用设置的那个。


代码注释

Note that I would like to avoid writing 64 bit versions of all my functions if possible.

OP 想要 "would like to upgrade to int64_t" 但 "avoid writing 64 bit versions of all my functions"。这是最奇怪的,而且显然是矛盾的。

或许re-writeinternal.cint64_t然后调用select回调函数

typedef int (*api_func)(const int, const my_long, const int) 中的 3 个 const 毫无用处。

OP 似乎假定 int 是 32 位给定标题。最好编写不假设这一点的代码。 int 至少是 16 位。 OTOH,假设 int 不比 int64_t 宽,我认为没有什么大问题,但即使是 C 规范也没有定义。

OP 似乎也假设 long longint64_t 给定代码 #define MYLONG long long 而 "library would always be compiled with #define MYLONG int64_t"。 OP 需要在代码和文档中正确且一致地使用类型。