【问题标题】:__attribute__((constructor)) equivalent in VC?VC 中的 __attribute__((constructor)) 等价物?
【发布时间】:2020-05-29 23:01:43
【问题描述】:

我想知道是否可以在 VC 中使用 C 构造函数,就像在 GCC 中使用它们一样。
gcc 方式使用__attribute__ 关键字非常直接,不幸的是VC 似乎甚至不知道这个关键字,因为我不是Win32 程序员我想知道这些东西是否有某种等效的关键字。
请注意 - 这是一个 C 程序,甚至不是 C++ 或 C#,(因为在那些语言中很容易做到这一点)

【问题讨论】:

  • 对于我们这些没有使用过它的人来说,它有什么作用? (你需要它做什么?)
  • 任何标记为constructor 的函数都由动态链接器在加载对象时运行。

标签: c visual-studio


【解决方案1】:

我尝试了 MSVC 中的最后一个答案,例如

#ifdef _MSC_VER
#pragma section(".CRT$XCU",read)
#define INITIALIZER2_(f,p) \
        static void f(void); \
        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void f(void)
#ifdef _WIN64
#define INITIALIZER(f) INITIALIZER2_(f,"")
#else
#define INITIALIZER(f) INITIALIZER2_(f,"_")
#endif
#else
#define INITIALIZER(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif

但是 INITIALIZER(f) 不能出现在 2 个不同的文件中,具有相同的函数名称传递给 INITIALIZER,以下定义将允许这样做

#ifdef _MSC_VER
#define INITIALIZER(f) \
    static void f();\
    static int __f1(){f();return 0;}\
    __pragma(data_seg(".CRT$XIU"))\
    static int(*__f2) () = __f1;\
    __pragma(data_seg())\
    static void f()
#else
#define INITIALIZER(f) \
    __attribute__((constructor)) static void f()
#endif

【讨论】:

  • 我的答案中的链接器编译指示是必需的,并且确实具有初始化函数名称对于整个 exe/dll 必须是唯一的效果。请参阅 user3042599 在我的示例下的 cmets,以及我的回复。我不知道更改部分名称是否是链接器杂注的替代解决方案,但它会产生其他副作用。
【解决方案2】:

下面的 C 代码演示了如何定义一个 void(void) 函数,以便在程序/库加载时调用,在 main 执行之前。

对于 MSVC,这会在用户初始化程序部分 (.CRT$XCU) 中放置一个指向函数的指针,这与编译器对静态 C++ 对象的构造函数调用所做的基本相同。对于 GCC,使用构造函数属性。

    // Initializer/finalizer sample for MSVC and GCC/Clang.
    // 2010-2016 Joe Lowe. Released into the public domain.
#include <stdio.h>
#include <stdlib.h>

#ifdef __cplusplus
    #define INITIALIZER(f) \
        static void f(void); \
        struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \
        static void f(void)
#elif defined(_MSC_VER)
    #pragma section(".CRT$XCU",read)
    #define INITIALIZER2_(f,p) \
        static void f(void); \
        __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \
        __pragma(comment(linker,"/include:" p #f "_")) \
        static void f(void)
    #ifdef _WIN64
        #define INITIALIZER(f) INITIALIZER2_(f,"")
    #else
        #define INITIALIZER(f) INITIALIZER2_(f,"_")
    #endif
#else
    #define INITIALIZER(f) \
        static void f(void) __attribute__((constructor)); \
        static void f(void)
#endif

static void finalize(void)
{
    printf( "finalize\n");
}

INITIALIZER( initialize)
{
    printf( "initialize\n");
    atexit( finalize);
}

int main( int argc, char** argv)
{
    printf( "main\n");
    return 0;
}

【讨论】:

  • 嗨乔:很棒的帖子!我真的需要与 VC 合作的技巧。在 C(不是 C++)中自动注册单元测试函数至关重要。顺便说一句:宏 INITIALIZER 的第二行应该有一个拖尾 \
  • 此类构造函数将在新的 Visual Studio 发布版本中得到优化。它的rerely已知和不幸的是未解决的错误。到目前为止,我发现的唯一解决方法是:必须禁用项目属性 > C / C++ > 优化 > 整个程序优化(/ GL)。
  • 我遇到了与上述相同的问题,所以不要只是复制粘贴上面的代码,假设它会正常工作。
  • GLib 最近从使用 .CRT$XCU 部分切换到使用 DllMain,原因是:bugzilla.gnome.org/show_bug.cgi?id=752837
  • @user3042599。感谢您对 MSVC 2015 链接时间优化问题的提醒。我已经编辑了代码示例,它应该没有问题,尽管由于使用 __pragma() 现在它需要 MSVC 2008 或更高版本。
【解决方案3】:

我认为没有办法避免在 MSVC 中使用 C++ 功能。 (无论如何,MSVC 的 C 支持很烂。)

未经测试,但这至少应该允许相同的代码在 MSVC 和 GCC 中工作。

#if defined(_MSC_VER)
struct construct { construct(void (*f)(void)) { f(); } };
#define constructor(fn) \
    void fn(void); static constructor constructor_##fn(fn)
#elif defined(__GNUC__)
#define constructor(fn)
    void fn(void) __attribute__((constructor))
#endif

static constructor(foo);
void foo() {
    ...
}

【讨论】:

  • 据我所知,这在 MSVC 2013 或 2015 上的纯 C 中不起作用。似乎 C 编译器模式不支持结构中的构造函数。我试图让它以各种方式工作。
【解决方案4】:

您可能对DllMain 感兴趣。

【讨论】:

  • 必须在进入main()之前执行,这是针对dll的,有什么关系? ://
  • 关于 __attribute__((constructor)) 的唯一真正用途是在共享库中使用它们,类似于模拟 DllMain :)
  • __attribute__((constructor)) 即使在单个程序映像中也很有用;例如,在库和系统调用周围插入全局挂钩,或注册内置“插件”,或初始化动态链接模块在其“DllMain”-likes 中需要的数据结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-13
  • 2014-10-26
  • 1970-01-01
  • 2011-04-30
  • 1970-01-01
相关资源
最近更新 更多