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_t
或 int64_t
.
否则使用原生 int
或 long
。
要求用户为完全不必要的东西定义宏是容易出错的错误。
- 安全吗?
没有。 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)
每当 int
与 int64_t
的类型不同时,这就是 UB(不是溢出)。
- 我是否依赖编译器的任何 non-guaranteed 行为?
是的。
- 为什么它会(或不会)起作用? (规范中有关于此的段落吗?)
这是未定义的行为 (UB)。代码正在调用具有一个签名的函数,但该函数可能具有不兼容的签名。
- 您认为有更好的方法吗?
是的。
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.c
为int64_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 long
是 int64_t
给定代码 #define MYLONG long long
而 "library would always be compiled with #define MYLONG int64_t
"。 OP 需要在代码和文档中正确且一致地使用类型。
我有一个 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_t
或 int64_t
.
否则使用原生 int
或 long
。
要求用户为完全不必要的东西定义宏是容易出错的错误。
- 安全吗?
没有。 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)
每当 int
与 int64_t
的类型不同时,这就是 UB(不是溢出)。
- 我是否依赖编译器的任何 non-guaranteed 行为?
是的。
- 为什么它会(或不会)起作用? (规范中有关于此的段落吗?)
这是未定义的行为 (UB)。代码正在调用具有一个签名的函数,但该函数可能具有不兼容的签名。
- 您认为有更好的方法吗?
是的。
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.c
为int64_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 long
是 int64_t
给定代码 #define MYLONG long long
而 "library would always be compiled with #define MYLONG int64_t
"。 OP 需要在代码和文档中正确且一致地使用类型。