I am trying to implement a C++ wrapper alongside my C project.
So, for example, I have "window.h" which has the standard C include guard. It requires <stdlib.h> and <string.h> as well as some third party library includes.
Then, I have "window.hpp" which depends on "window.h", but uses C++ #pragma once and requires <cstdlib> and <string> for the C++ implementations.
I am running into an ambiguity issue where the C/C++ standard implementations are conflicting, and I'm not quite sure what to do about it. You can see I tried to fix this by simply checking if __cplusplus was defined, and only adding the C++ headers if that's the case.
The idea is that you can just do using namespace LittleEngine::utils; and it will include SDL and GLEW for you, as well as add some wrapper features for creating a window, for example; where it uses C++ features like classes instead of the C implemented methods.
For sanity's sake, that's why these are separate files and not just one header file that adds compatibility for both C and C++ with extern "C" { ... }.
"window.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;
};
main.cpp
#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;
}
So after some more testing, the answer to my problem was that you have to wrap C header files in extern "C" before trying to include them into namespaces.
namespace LittleEngine {
namespace utils {
extern "C" {
# include "window.h"
}
# include "window.hpp"
}
}
However, even when doing this, it still puts the C-defines at the root namespace, which makes sense I guess.
Edit: Moreover, it does get a lot weirder than just that. In the above example, I can extern-C include the "window.h" file. The second I hop to another file and try to chain impl. this, I get the same error I started out with. TLDR; TIL only C++ includes go in namespaces.
Edit 2: I think it was just GLEW and SDL causing the issue. I hate programming. Why do I do this to myself?
Edit 3: After even more testing.. I think it was. So the biggest changes I made when refactoring is
Not including headers in sub-header files. You can do this because of how the Linker works.
I took out all the extern "C" because all that should theoretically do is disable C++ name wrangling, which doesn't make sense anyway.
I simply moved the SDL and GLEW includes out of scope of the namespace and suddenly it works.
Either way, my point still stands. Don't include C headers in C++ namespaces.
If somebody else wants to answer this in a preferably more detailed manner, I would be happy to mark that as the answer.
Related
everytime i try to compile it to test it doesnt recongnise files
#include <GL\glew.h>
#include <GLFW/glfw3.h>
#ifdef _WIN32
#pragma comment(lib, "winmm.lib")
#endif // _WIN32
int main(void)
{
GLFWwindow* window;
/* Initialize the library */
if (!glfwInit())
return -1;
/* Create a windowed mode window and its OpenGL context */
window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
if (!window)
{
glfwTerminate();
return -1;
}
/* Make the window's context current */
glfwMakeContextCurrent(window);
if(!glewInit())
return -1;
/* Loop until the user closes the window */
while (!glfwWindowShouldClose(window))
{
/* Render here */
glClear(GL_COLOR_BUFFER_BIT);
/* Swap front and back buffers */
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
glfwTerminate();
return 0;
}
When you are using a compiler you need to tell it the directory to find files that you include.
In the line
#include <GL\glew.h>
#include <GLFW/glfw3.h>
This is telling the compiler to include the files glew.h and glfw3.h.
The problem is how is the compiler supposed to know where those files are (it doesn't search your entire Drive for them. c++ compilers have a way for you to tell it where these files are relative to.
In gcc/g++ you use the -I flag and specify where this directory is.
specify the folders where you stored gl\glew.h and glfw\glfw3.hto the gcc using that -I option. If your files are store in C:\Users\John\Projects\dependencies\GLFW\include\glew\glew.h and C:\Users\John\Projects\dependencies\Glew\include\glfw\glfw3.h
then use the the flags like so
gcc -IC:\Users\John\Projects\dependencies\GLFW\include
-IC:\Users\John\Projects\dependencies\Glew\include main.c -o out_file
I'm trying to run a basic Vulkan test using GLFW3.2 :
#include <vulkan/vulkan.h>
#include <GLFW/glfw3.h>
#include <iostream>
int main(int argc, char *argv[])
{
if (glfwVulkanSupported())
{
std::cout << "vulkan supported !" << std::endl;
}
else
{
std::cout << "vulkan NOT supported !" << std::endl;
}
return 0;
}
Which i build using the following cmake :
cmake_minimum_required(VERSION 3.5.1)
project(vktest)
set (CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER "/usr/bin/g++-4.9")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(Vulkan REQUIRED)
find_package(glfw3 3.2 REQUIRED)
include_directories(${Vulkan_INCLUDE_DIRS})
message("kek")
message(${Vulkan_INCLUDE_DIRS})
message(${Vulkan_LIBRARIES})
message(${Vulkan_LIBRARY})
add_executable(vktest vulkanTest.cpp)
target_link_libraries(vktest ${Vulkan_LIBRARIES})
target_link_libraries(vktest glfw)
Building this works just fine.
But when i run the program i always get in the else statement.
I installed nvidia last driver, lunarg's vulkan sdk with the following
environment variables :
export VULKAN_SDK=/home/mathias/vulkan/VulkanSDK/1.0.42.2/x86_64
set PATH $VULKAN_SDK/bin $PATH
set LD_LIBRARY_PATH $VULKAN_SDK/lib $LD_LIBRARY_PATH
set VK_LAYER_PATH $VULKAN_SDK/etc/explicit_layer.d
Besides i can run the cube example.
Any idea of why it never returns true ?
You are probably missing a glfwInit() call.
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <cassert>
int main(int argc, const char** argv) {
bool success = glfwInit(); //< Important!
assert(success);
if(glfwVVulkanSupported()) {
puts("Success!");
}
return 0;
}
Generally, the only glfw function you should call before glfwInit() is glfwSetErrorCallback. (There may be other exceptions, but none that I am aware of.)
Your code is missing #define GLFW_INCLUDE_VULKAN , does it not?
#define GLFW_INCLUDE_VULKAN //as required by GLFW
#include "vulkan\vk_cpp.hpp"
#include "GLFW\glfw3.h"
#include <iostream>
#include <algorithm>
#include <iterator>
int main()
{
if (!glfwInit()) //Initialize GLFW
{
std::cout << "GLFW not initialized.\n";
abort();
}
if (!glfwVulkanSupported()) //Any Vulkan-related function requires GLFW initialized
{
std::cout << "Vulkan not supported.\n";
abort();
}
uint32_t ext_count; // an output placeholder
auto ext_list = glfwGetRequiredInstanceExtensions(&ext_count);
std::copy(&ext_list[0], &ext_list[ext_count-1], std::ostream_iterator<const char *>(cout, "\n"));
//feed extension list into InstanceCreateInfo struct in prepare for Vulkan instance creation
vk::InstanceCreateInfo ifo{ vk::InstanceCreateFlags(), nullptr, 0, nullptr, ext_count, ext_list };
glfwDefaultWindowHints();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // Comment out this if you need OpenGL also
GLFWwindow* hWnd = glfwCreateWindow(640, 480, "Foobar", NULL, NULL); // The usual window
auto inst = vk::createInstance(ifo); // Vulkan instance creation
vk::SurfaceKHR surf{};
auto psurf = VkSurfaceKHR(surf); // you can cast vk::* object to Vk* using a type cast
auto r = glfwCreateWindowSurface(VkInstance(inst), hWnd, NULL, &psurf); //VK_SUCCESS is somehow...0
// Draw stuff here
// Finish drawing
vkDestroySurfaceKHR((VkInstance)inst, VkSurfaceKHR(surf), nullptr);//vkcpp seems have no destructor for surface object
glfwDestroyWindow(hWnd); //Destroy the surface before the window
glfwTerminate(); // Free GLFW resource
return 0;
}
if there are typos in this code free to fix them. I have no way to compile it at current location
I'm getting the error LNK2005 for seemingly no reason, or at least none that can I can identify. Essentially, using the following code I can compile without issue.
test.h
#define GLEW_STATIC
#include <GL\glew.h>
#include <GLFW\glfw3.h>
namespace test
{
//Objects and variables
GLFWwindow* window;
//Function prototypes
bool Initialize();
}
test.cpp
#include "test.h"
#include <iostream>
bool test::Initialize()
{
std::cout << "Initializing GLFW: OpenGL version 3.3 \n";
//Initialize GLFW
glfwInit();
//Set window properties (version 3.3, core profile, not resizeable)
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
//Create Window
window = glfwCreateWindow(800, 800, "Learn OpenGL", nullptr, nullptr);
if (window = nullptr)
{
std::cout << "Failed to create GLFW window \n";
glfwTerminate();
return false;
}
return true;
}
int main()
{
test::Initialize();
return 0;
}
However, when compiling nearly the exact same thing (http://pastebin.com/VpPep9pM), along with some other code, it gives the errors:
Error LNK2005 "struct GLFWwindow * window" (?window##3PAUGLFWwindow##A) already defined in Main.obj OpenGL D:\Users\Matthew\documents\visual studio 2015\Projects\OpenGL\OpenGL\System.obj
Error LNK2005 "struct GLFWwindow * System::window" (?window#System##3PAUGLFWwindow##A) already defined in Main.obj OpenGL D:\Users\Matthew\documents\visual studio 2015\Projects\OpenGL\OpenGL\System.obj
Error LNK1169 one or more multiply defined symbols found OpenGL D:\Users\Matthew\documents\visual studio 2015\Projects\OpenGL\Debug\OpenGL.exe
So, I would like to know what causes the errors, I'm assuming it has something to do with the "context".
In your header file, you are defining you variable, when it needs to be declared in the header and defined in one source file.
In test.h
namespace test {
extern GLFWwindow* window;
}
and in main.cpp
namespace test {
GLFWwindow* window;
}
Without the extern in the header, you create one instance of test::window in every source file that includes it, which is a problem if there are two or more definitions.
I am currently attempting to write a very simple 2d game engine using SDL in order to help myself get more comfortable with coding. Unfortunately, I made the mistake of starting with a single source file, and now that my code is getting bigger, I am trying to split the existing code (which works fine before the split) into multiple header and source files. The first major issue I've encountered is that when I try to define SCREEN_HEIGHT, SCREEN_WIDTH, Window, and Renderer in init.cpp after declaring them in init.h, I get error: 'blank' does not name a type (blank being Window, Renderer, etc.). I am also trying to make them global, which may be part of the issue. Included below is the relevant code (I took out all the stuff I think is irrelevant in this case). I suspect I'm just missing something really simple here, but I've been unable to find an existing answer online like I have with my previous problems.
main.cpp
#include "globals.h"
#include "texture.h"
....
globals.h
#ifndef GLOBALS
#define GLOBALS
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <stdio.h>
#include <string>
#endif //GLOBALS
texture.h
#ifndef TEXTURE
#define TEXTURE
#include "globals.h"
#include "init.h"
....
#endif // TEXTURE
texture.cpp
#include "texture.h"
....
init.h
#ifndef INIT
#define INIT
#include "globals.h"
//screen dimensions
int SCREEN_WIDTH;
int SCREEN_HEIGHT;
//initiates SDL and creates a window and renderer
bool init();
//the created window
SDL_Window* Window;
//the renderer that will be used
SDL_Renderer* Renderer;
#endif // INIT
init.cpp
#include "init.h"
SCREEN_WIDTH = 640;
SCREEN_HEIGHT = 480;
Window = NULL;
Renderer = NULL;
bool init()
{
bool success = true;
if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
{
printf( "SDL was unable to initialize! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
if( !SDL_SetHint( SDL_HINT_RENDER_SCALE_QUALITY, "1"))
{
printf( "Linear texture filtering not enabled!");
}
else
{
Window = SDL_CreateWindow( "Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if( Window == NULL)
{
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
Renderer = SDL_CreateRenderer( Window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if( Renderer == NULL)
{
printf( "Renderer could not be created! SDL Error: %s\n", SDL_GetError());
success = false;
}
else
{
SDL_SetRenderDrawColor( Renderer, 0xFF, 0xFF, 0xFF, 0xFF);
int PNGImage = IMG_INIT_PNG;
if( !(IMG_Init( PNGImage) & PNGImage))
{
printf( "Image loading not enabled! SDL_Image Error: %s\n", IMG_GetError());
success = false;
}
if( TTF_Init() == -1)
{
printf( "True type fonts not enabled! SDL_TTF Error: %s\n", TTF_GetError());
success = false;
}
}
}
}
}
return success;
}
I get the error in init.cpp at the top of the code before bool init()
Renderer = NULL; is an assignment statement, which you cannot have in global scope. You can only have declarations in global scope. Since you have already declared Renderer in your header file, you don't need to redeclare it either. Just set it to null within one of your initialization functions (e.g. your current init() function).
i'm sorry if the question layout is weird or something this is my first time asking a question. I have started learning c++ a week ago and now that i know how to create a window with GLFW I would like to clean up my code and make it nicer. I tried to put the "create window" stuff inside a .cpp file named "window.cpp" and then import it to the main file. but when i take out the GLFWCreateWindow function it doesn't recognize the window name "w_gill" in the swapbuffer, windowshouldclose and destroywindow functions. Can anyone please help me?
Here is the Main.cpp file:
#include <iostream>
#include <Windows.h>
#include "window.cpp"
int main(){
do{
createWindow();
glfwSwapBuffers(w_gill);
glfwPollEvents();
} while (!glfwWindowShouldClose(w_gill));
glfwDestroyWindow(w_gill);
glfwTerminate();
return 0;
}
And the Window.cpp file:
#include <GL\glew.h>
#include <GLFW\glfw3.h>
int windowWidth = 1920 / 2;
int windowHeight = 1080 / 2;
int createWindow(){
if (!glfwInit())
{
glfwTerminate();
return -1;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_VERSION_MINOR, 4);
glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);
GLFWwindow* w_gill;
w_gill = glfwCreateWindow(windowWidth, windowHeight, "Gillow", NULL, NULL);
if (!w_gill)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(w_gill);
glfwSetInputMode(w_gill, GLFW_STICKY_KEYS, GL_TRUE);
}
Nevermind I got it, I had to put
GLFWwindow* w_gill;
int createWindow();
inside of the main.cpp file so they are linked. Thanks for responding anyway.
Your problem has to do with the "scope" or lifetime that a variable has in C. The scope of w_gill is local to its containing function, and as such, there is no visibility to that variable outside of the function. In addition to moving the createWindow() function to its own .CPP file, you must have made some other changes, as even if it was still in main.cpp you would still have the same issue.