繁体   English   中英

避免 C++ 中的歧义

[英]Avoid ambiguity in C++

我正在尝试在我的 C 项目旁边实现一个 C++ 包装器。

因此,例如,我有"window.h" ,它具有标准的 C 包括防护。 它需要<stdlib.h><string.h>以及一些第三方库包括。

然后,我有依赖于 "window.h "window.hpp""window.h" ,但使用 C++ #pragma once并且需要<cstdlib><string>用于 C++ 实现。

我遇到了一个模棱两可的问题,其中 C/C++ 标准实现存在冲突,我不太确定该怎么做。 你可以看到我试图通过简单地检查是否定义了__cplusplus来解决这个问题,如果是这样的话,只添加 C++ 头文件。

这个想法是你可以using namespace LittleEngine::utils; 它将为您包括 SDL 和 GLEW,并添加一些用于创建 window 的包装器功能,例如; 它使用 C++ 特性(如类)而不是 C 实现的方法。

为了理智,这就是为什么这些是单独的文件,而不仅仅是一个 header 文件,它增加了对 C 和 C++ 与extern "C" {... }的兼容性。

“窗口.h”

#if __cplusplus
#pragma once
#endif

#ifndef LITTLE_ENGINE_UTILS_WINDOW_H_
#define LITTLE_ENGINE_UTILS_WINDOW_H_

#include <SDL2/SDL.h>
#include <GL/glew.h>

#if __cplusplus
#   include <cstdio>
#   include <string>
#else
#   include <stdio.h>
#   include <string.h>
#endif

typedef struct {
    SDL_Window* window;
    SDL_GLContext context;
    SDL_Event event;
} Engine_GLWindow_t;

enum Engine_Window_Renderer {
    VULKAN,
    OPENGL
};

int Engine_CreateGLWindow(const char* title, Engine_GLWindow_t* window)
{
    // Initialize SDL
    if(SDL_Init(SDL_INIT_EVERYTHING) != 0)
    {
        fprintf(stderr, "Failed to initialize SDL.\n");
        fprintf(stderr, "%s\n", SDL_GetError());
        return -1;
    }

    // Configure window flags
    Uint32 flags = 0;

    flags |= SDL_WINDOW_OPENGL;

    SDL_Window* sdlwin = SDL_CreateWindow(
        title,
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        800,
        600,
        flags
    );

    if(!sdlwin)
        return -1;

    window->window = sdlwin;

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
    SDL_GL_SetSwapInterval(1);

    SDL_GLContext context;
    if((context = SDL_GL_CreateContext(window->window)) != NULL)
        window->context = context;
    else
    {
        SDL_DestroyWindow(window->window);
        fprintf(stderr, "Failed to initialize the GL context.\n");
        fprintf(stderr, "%s\n", SDL_GetError());
        return -1;
    }

    glewExperimental = GL_TRUE;

    GLenum glewCode = glewInit();

    if(glewCode != GLEW_OK)
    {
        SDL_GL_DeleteContext(window->context);
        SDL_DestroyWindow(window->window);
        fprintf(stderr, "Failed to initialize GLEW.\n");
        fprintf(stderr, "%s\n", glewGetErrorString(glewCode));
        return -1;
    }

    glEnable(GL_DEPTH_TEST);

    glViewport(0, 0, 800, 600);

    SDL_ShowWindow(sdlwin);
    
    return 0;
}

int Engine_DestroyGLWindow(Engine_GLWindow_t* window)
{
    SDL_GL_DeleteContext(window->context);
    SDL_DestroyWindow(window->window);
    SDL_Quit();

    return 0;
}
#endif

“windows.hpp”

#pragma once

/*
file included as:
engine.hpp
...
namespace LittleEngine {
    namespace utils {
#       include "utils.hpp" // -> #include "windows.hpp"
    }
}
*/

#include "window.h"

#include <cstdint>

class GLWindow {
public:

    GLWindow(std::string title)
    {
        Engine_CreateGLWindow(title.c_str(), &this->window_data);
    }

    GLWindow(std::string title, bool& success)
    {
        if(Engine_CreateGLWindow(title.c_str(), &this->window_data) == 0)
            success = true;
    }

    ~GLWindow()
    {
        Engine_DestroyGLWindow(&this->window_data);
    }

    Engine_GLWindow_t* getWindowData()
    {
        return &this->window_data;
    }

protected:
    Engine_GLWindow_t window_data;
};

主文件

#include <LittleEngine/engine.hpp>

int main(int argc, char* argv[], char* envp[])
{
    // COMMENTED CODE WORKS BTW
    // Engine_GLWindow_t window;
    // Engine_CreateGLWindow("LittleBird", &window);

    LittleEngine::utils::GLWindow window("LittleBird");

    bool should_run = true;
    const LittleEngine::utils::Uint8* keys = nullptr;
    
    while (should_run)
    {
        while (LittleEngine::utils::SDL_PollEvent(&window.getWindowData()->event))
            if (window.getWindowData()->event.type == LittleEngine::utils::SDL_QUIT)
                should_run = false;

        // ensure that SDL2 updated the keyboard state
        LittleEngine::utils::SDL_PumpEvents();

        // // automatically update window size
        // UpdateWindowSize(&window);

        // SDL2 should automatically clear this memory after each loop
        keys = LittleEngine::utils::SDL_GetKeyboardState(0);

        // black background color
        LittleEngine::utils::glClearColor(0.00f, 0.00f, 0.00f, 1.00f);

        // red background color if pressing W
        if(keys[LittleEngine::utils::SDL_SCANCODE_W])
            LittleEngine::utils::glClearColor(1.00f, 0.00f, 0.00f, 1.00f);
        
        LittleEngine::utils::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        LittleEngine::utils::SDL_GL_SwapWindow(window.getWindowData()->window);
    }
    return 0;
}

日志

因此,经过更多测试后,我的问题的答案是,您必须先将 C header 文件包装在extern "C"中,然后再尝试将它们包含到命名空间中。

namespace LittleEngine {
    namespace utils {
        extern "C" {
#           include "window.h"
        }
#       include "window.hpp"
    }
}

但是,即使这样做,它仍然将 C-defines 放在根名称空间中,我想这是有道理的。

编辑:此外,它确实比这更奇怪。 在上面的例子中,我可以通过 extern-C 包含“window.h”文件。 第二个我跳到另一个文件并尝试链接 impl。 这个,我得到了与我开始时相同的错误。 TLDR; 仅 TIL C++ 在命名空间中包含 go。

编辑 2:我认为这只是 GLEW 和 SDL 造成的问题。 我讨厌编程。 我为什么要这样对自己?

编辑3:经过更多测试..我认为是。 所以我在重构时所做的最大改变是

  1. 不包括子头文件中的头文件。 您可以这样做是因为 Linker 的工作原理。
  2. 我取出了所有的extern "C" ,因为理论上应该做的就是禁用 C++ 名称争论,这无论如何都没有意义。
  3. 我只是将 SDL 和 GLEW 包含在命名空间的 scope 之外,突然它就可以工作了。

不管怎样,我的观点仍然成立。 不要在 C++ 命名空间中包含 C 标头。

如果其他人想以更详细的方式回答这个问题,我很乐意将其标记为答案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM