如何在编译时检查函数是否在全局范围内声明

时间:2016-03-11 15:28:07

标签: c++ templates opengl

我有一个标题,例如#include <GL/gl.h>。它包含OpenGL API函数的子集。我需要这样的东西:

static_assert(has_glDrawArraysIndirect::value, "There is no glDrawArraysIndirect");

甚至更好:

PFNGLDRAWARRAYSINSTANCEDPROC ptr_glDrawArraysIndirect = ptr_to_glDrawArraysIndirect::ptr;

其中ptr_to_glDrawArraysIndirect::ptr展开指向glDrawArraysIndirect(如果已定义),或者指向存根函数stub_glDrawArraysIndirect

我的目标操作系统非常具体。任何基于链接器的解决方案(如GetProcAddressdlsym)对我都不起作用,因为没有动态链接器。不仅如此,我的驱动程序还没有提供glXGetProcAdrresswglGetProcAddress,基本上没有办法在运行时通过函数名查询指针(实际上,我想实现这样的机制)。

有什么想法吗?

4 个答案:

答案 0 :(得分:6)

这是一个可以在编译时检测它并产生一个布尔值的答案。它的工作原理是在命名空间中创建同名的模板函数,然后在is_defined()函数内使用该命名空间。如果真实的glDrawArraysIndirect()存在,它将优先于模板版本。如果你注释掉glDrawArraysIndirect()的第一个声明,那么底部的静态断言就会触发。

Test on GodBolt

#include <type_traits>

enum GLenum {};

void glDrawArraysIndirect(GLenum, const void*);

namespace detail {
    struct dummy;

    template<typename T>
    dummy& glDrawArraysIndirect(T, const void*);
}

constexpr bool is_defined()
{
    using namespace detail;
    using ftype = decltype(glDrawArraysIndirect(GLenum(), nullptr));
    return std::is_same<ftype, void>();
}

static_assert(is_defined(), "not defined");

通过一些调整,您可以将自定义功能作为模板并使用类似的技巧

ideone.com

#include <type_traits>
#include <iostream>

//#define USE_REAL

enum GLenum {TEST};

typedef void (*func_type)(GLenum, const void*);

#ifdef USE_REAL
void glDrawArraysIndirect(GLenum, const void*);
#endif

namespace detail {
    struct dummy {};

    template<typename T = dummy>
    void glDrawArraysIndirect(GLenum, const void*, T = T())
    {
        std::cout << "In placeholder function" << std::endl;
    }

}

void wrapDraw(GLenum x, const void* y)
{
  using namespace detail;
  glDrawArraysIndirect(x, y);
}

#ifdef USE_REAL
void glDrawArraysIndirect(GLenum, const void*)
{
    std::cout << "In real function" << std::endl;
}
#endif

int main()
{
    wrapDraw(TEST, nullptr);
}

答案 1 :(得分:3)

在某处包含表达式sizeof(::function)。 (如果函数存在,那么询问函数指针的大小是完全有效的事情)。

在运行时它是良性的,::强制使用在全局范围内声明的function

当然,如果全局范围内不存在function,则编译将失败。

除了其他错误之外,如果要在

行上写一些内容,编译器将发出特定错误

static_assert(sizeof(::function), "There is no global function");

答案 2 :(得分:1)

  

我的目标操作系统非常具体。任何基于链接器的解决方案(如GetProcAddress或dlsym)对我都不起作用,因为没有动态链接器。

这是一个嵌入式系统还是只是在标准PC硬件上运行的奇怪的操作系统?

  

不仅如此,我的驱动程序还没有提供glXGetProcAdrress和wglGetProcAddress,基本上没有办法在运行时通过函数名查询指针

在运行时查询函数指针的依赖性不依赖于动态链接器的存在。这两个是完全正交的,甚至纯粹静态链接的嵌入式OpenGL实现也可以提供GetProcAddress接口。我不是试图以某种方式在编译或链接时解决问题,而是通过为您的OpenGL驱动程序实现GetProcAddress来解决问题;即使驱动程序仅作为二进制形式的静态库可用,也可以这样做。第一步:

为每个OpenGL函数创建函数指针存根,静态初始化为NULL并归因于弱链接。将其链接到静态库,您可以调用gl_null_stubs或类似的。

创建一个GetProcAddress函数,对于每个OpenGL函数,返回指向函数编译单元范围内的函数符号的指针。

现在将您奇怪的OpenGL驱动程序与存根库和GetProcAddress实现链接起来。对于每个函数,存根的弱连接将静态库符号优先。对于驱动程序中没有的所有OpenGL符号,存根将接管。

那里:现在你有一个具有 GetProcAddress实现的OpenGL驱动程序库。那不是那么难,是吗?

答案 3 :(得分:1)

  

如何在编译时检查函数是否在全局范围内声明?   我的目标操作系统非常具体......

可能的解决方案可能是,如果您使用最近的GCC - 可能作为奇怪的目标操作系统和ABI的交叉编译器 - 来自定义 HttpCookie authCookie = HttpContext.Request.Cookies["Id"]; (或gcc etc ...)编译器使用您自己的MELT扩展名。

MELT是一种领域特定语言,作为免费软件GCC插件(主要在Linux上)实现,用于自定义GCC编译器。