Calling std::thread() around a function works differently - c++

Is there any reason why this code here:
int main(int argc, char* argv[])
{
Main::Init();
std::thread worker(Main::Mainloop);
worker.join();
Main::Free();
return 0;
}
should work differently to this code here:
int main(int argc, char* argv[])
{
Main::Init();
Main::Mainloop();
Main::Free();
return 0;
}
Noting that the Main class is defined as a singleton, here is the code:
main.h
#pragma once
#ifndef MAIN_H
#define MAIN_H
#include "window.h"
#include "mainloop.h"
class Main ///Singleton
{
public:
Main(const Main&) = delete;
Main(Main&&) = delete;
Main& operator=(const Main&) = delete;
Main& operator=(Main&&) = delete;
private:
Main();
static Main& Get_Instance();
friend int main(int argc, char* argv[]);
static void Mainloop();
static void Init();
static void Free();
};
#endif // MAIN_H
The first example above fails to initialise one ofGLFW, GLEW, and ImGui which are what I am using for my program. I was trying to split up the initialisation of the program but then i ran into this issue. When I dug farther I reached this point which doesn't really make any sense why it shouldn't work. Basically it either throws an exception or ImGui spams me with many errors during runtime saying:
failed to compile vertex shader!
failed to compile fragment shader!
failed to link shader program! (with GLSL `#version 460`)
yet the window opens up and I only get these during runtime with the thread example. Not with the other one.

All of those libraries in some way interact with OpenGL and therefore are very sensitive to what thread they're being executed on. The current OpenGL context is thread-specific; each thread has its own current context, and a context can only be current within one thread at any time.
Creating a GLFW window creates an OpenGL context. If you then switch to another thread, that context will not be current in that thread unless you tell GLFW to make it current in that thread.

Related

Debug Assertion Failed Expression __acrt_first_block == header

I have been trying to figure out why this is happening and maybe it is just due to inexperience at this point but could really use some help.
When I run my code, which is compiled into a DLL using C++20, I get that a debug assertion has failed with the expression being __acrt_first_block == header.
I narrowed down where the code is failing, but the weird part is that it runs just fine when I change the Init(std::string filePath function signature to not contain the parameter. The code is below and hope someone can help.
Logger.h
#pragma once
#include "../Core.h"
#include <memory>
#include <string>
#include "spdlog/spdlog.h"
namespace Ruby
{
class RUBY_API Logger
{
public:
static void Init(std::string filePath);
inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() { return coreLogger; }
inline static std::shared_ptr<spdlog::logger>& GetClientLogger() { return clientLogger; }
private:
static std::shared_ptr<spdlog::logger> coreLogger;
static std::shared_ptr<spdlog::logger> clientLogger;
};
}
Logger.cpp
namespace Ruby
{
std::shared_ptr<spdlog::logger> Logger::coreLogger;
std::shared_ptr<spdlog::logger> Logger::clientLogger;
void Logger::Init(std::string filePath)
{
std::string pattern{ "%^[%r][%n][%l]: %v%$" };
auto fileSink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filePath, true);
// Setup the console and file sinks
std::vector<spdlog::sink_ptr> coreSinks;
coreSinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
coreSinks.push_back(fileSink);
// Bind the sinks to the core logger.
coreLogger = std::make_shared<spdlog::logger>("RUBY", begin(coreSinks), end(coreSinks));
// Set the Patterns for the sinks
coreLogger->sinks()[0]->set_pattern(pattern);
coreLogger->sinks()[1]->set_pattern(pattern);
// Tell spdlog to flush the file loggers on trace or worse message (can be changed if necessary).
coreLogger->flush_on(spdlog::level::trace);
// Set the default level of the logger
coreLogger->set_level(spdlog::level::trace);
// Do the same for the client logger
std::vector<spdlog::sink_ptr> clientSinks;
clientSinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
clientSinks.push_back(fileSink);
clientLogger = std::make_shared<spdlog::logger>("APP", begin(clientSinks), end(clientSinks));
clientLogger->sinks()[0]->set_pattern(pattern);
clientLogger->sinks()[1]->set_pattern(pattern);
clientLogger->flush_on(spdlog::level::trace);
clientLogger->set_level(spdlog::level::trace);
}
}
Entrypoint.h
#pragma once
#ifdef RB_PLATFORM_WINDOWS
extern Ruby::Application* Ruby::CreateApplication();
int main(int argc, char** argv)
{
Ruby::Logger::Init("../Logs/Recent_Run.txt");
RB_CORE_INFO("Initialized the logger.");
auto app = Ruby::CreateApplication();
app->Run();
delete app;
return 0;
}
#else
#error Ruby only supports windows
#endif // RB_PLATFORM_WINDOWS
For anyone else who runs into a similar problem, here is how I fixed it.
Essentially the function signature for the Init() function was the problem. The std::string parameter was causing the debug assertion to fire, my best guess as of right now was because of move semantics but that part I am still not sure on. So there are a couple of ways that I found to fix this.
Method 1:
Make the parameter a const char*. I don't quite like this approach as it then relies on C style strings and if you are trying to write a program in modern C++, this is a huge step backwards.
Method 2:
Make the parameter a const std::string&. Making it a const reference to a string prevents the move semantics (again as far as I know) and the assertion no longer fires. I prefer this fix as it keeps the program in modern C++.
I hope this helps anyone who has similar issues, and be careful with statics and move semantics.

"Field is never used" although its (field's) constructor runs

class SdlManager
{
public:
SdlManager();
~SdlManager();
};
class Application
{
SdlManager sdlManager;
Screen screen;
EventHandler eventHandler;
bool running = true;
int fps = Fps;
void Draw();
public:
Application();
void Run();
void Stop();
};
int main(int argc, char* argv[])
{
Application app;
app.Run();
return 0;
}
Hi. I'm toying with SDL using Clion and I noticed this warning which seems strange: it complains that the field sdlManager is never used, but I'm sure (even using breakpoints) that the program runs sdlManager's constructor when I instantiate an Application object inside main.
What should I do? Is it a Clion's (or whatever plugin it uses) bug?
sdlManager is constructed, but it's never used after that. That's why you're getthing the warning.
Once you'll add methods to it, and start using them, the warning will go away.
The point of that warning is to signal code that isn't used anywhere in the code, either someone forgot to use it, or it needs to be removed (after some refactoring or something).

Linker warning causes random linking errors every few compilations

I can't get a minimal reproduceable example any smaller than what I have. Mainly because I can't debug the issue, it seems completely random and I don't consistently get the error, sometimes it compiles, sometimes it doesn't. I have reverted back to when I had the error so I can copy the code, but since then, I haven't gotten an error. Basically, I have no other ideas left other than to fix all my linker warnings. I remember it was mostly related to this warning LNK4042 object specified more than once; extras ignored in main.obj. There is also another warning I don't understand, that being I have the binaries for a library named GLEW, yet statically linking it invokes a linker warning, LNK4099 PDB 'vc120.pdb' was not found with 'glew32s.lib(glew.obj)' or at '\SolutionDir\Debug\vc120.pdb'; linking object as if no debug info
What are the fixes for these warnings? Hopefully to stop me randomly getting errors that, when I change one thing it compiles, and then revert it back and it still compiles.
Here is the code, main.h
#pragma once
#ifndef MAIN_H
#define MAIN_H
#include <GL/glew.h> // Initialize with glewInit()
#ifndef GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_NONE // GLFW including OpenGL headers causes ambiguity or multiple definition errors.
#endif // GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "ImGui/imgui.h"
#include "ImGui/implot.h"
#include "ImGui/imgui_impl_glfw.h"
#include "ImGui/imgui_impl_opengl3.h"
#include <iostream> //std::string
class Main ///Singleton
{
public:
Main(const Main&) = delete;
Main(Main&&) = delete;
Main& operator=(const Main&) = delete;
Main& operator=(Main&&) = delete;
private:
Main();
static Main& Get_Instance();
friend int main(int argc, char* argv[]);
static void Mainloop();
static void Init();
static void Free();
GLFWwindow* m_Window = nullptr;
std::string m_GLSL_Version = "";
int m_Window_Width = 1280;
int m_Window_Height = 720;
};
#endif // MAIN_H
and main.cpp
#include "main.h"
Main::Main()
{
}
Main& Main::Get_Instance()
{
static Main instance;
return instance;
}
void Main::Init()
{
// Setup window
Get_Instance(); //Init constructor
if (!glfwInit()) {
std::cout << "Could not initialize GLFW" << std::endl;
std::cin.get();
exit(EXIT_FAILURE);
}
Get_Instance().m_GLSL_Version = "#version 460";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
// Create window with graphics context
Get_Instance().m_Window = glfwCreateWindow(Get_Instance().m_Window_Width, Get_Instance().m_Window_Height, "Program", NULL, NULL);
if (Get_Instance().m_Window == NULL) {
std::cout << "Could not create GLFW window" << std::endl;
std::cin.get();
exit(EXIT_FAILURE);
}
glfwMakeContextCurrent(Get_Instance().m_Window);
glfwSwapInterval(0); // vsync
if (glewInit() != GLEW_OK)
{
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
std::cin.get();
exit(EXIT_FAILURE);
}
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImPlot::CreateContext();
// Setup Dear ImGui style
ImGui::StyleColorsClassic();
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(Get_Instance().m_Window, true);
ImGui_ImplOpenGL3_Init(Get_Instance().m_GLSL_Version.c_str());
}
void Main::Mainloop()
{
// Main loop
while (!glfwWindowShouldClose(Get_Instance().m_Window))
{
//Events
glfwPollEvents();
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
ImGui::Text("%.3f", ImGui::GetIO().Framerate);
// Rendering
ImGui::Render();
glfwGetFramebufferSize(Get_Instance().m_Window, &Get_Instance().m_Window_Width, &Get_Instance().m_Window_Height);
glViewport(0, 0, Get_Instance().m_Window_Width, Get_Instance().m_Window_Height);
glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(Get_Instance().m_Window);
}
}
void Main::Free()
{
// Cleanup
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
glfwDestroyWindow(Get_Instance().m_Window);
glfwTerminate();
}
int main(int argc, char* argv[])
{
Main::Init();
Main::Mainloop();
Main::Free();
return 0;
}
Edit: It happened again, here are the errors:
Error LNK1561 entry point must be defined
Warning LNK4042 object specified more than once; extras ignored (THIS IS IN MY main.obj file, its the only linker issue that references my actual files, the rest reference either 'LINK' or 'libucrt.lib(something here)'
Error LNK2005 __cexit already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __crt_atexit already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __crt_at_quick_exit already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __execute_onexit_table already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __initialize_narrow_environment already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __initialize_onexit_table already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __register_onexit_function already defined in ucrtd.lib(ucrtbased.dll)
Error LNK2005 __seh_filter_dll already defined in ucrtd.lib(ucrtbased.dll)
Regarding the LNK4042 warning, you may be able to resolve the issue, if you open the properties for the entire project, and the change the value under C/C++ -> Output Files -> "Object File Name" to be the following:
$(IntDir)/%(RelativeDir)/

Is this use of global static initialization okay for a "busybox" style application?

Still working on my closed/open source hybrid talked about earlier in this question. The application in question is actually a busybox like application -- there are several programs bundled into a single program, and the actual program run is chosen based upon the first command entered. This allows the sub-programs to share one copy of the CRT (I can't use the installable redist because I need to maintain single binary deployment), as well as several internals which are useful in several of the sub programs.
Because some of the sub programs themselves cannot be released as a result of licensing restrictions, I'm considering using a startup like this. (Sorry for the amount of code :( )
ISubProgram.hpp
#include <string>
struct ISubProgram
{
virtual std::wstring GetExecutive() const = 0; //Return sub program name
virtual void OnStart(int argc, const char *argv[]) {};
virtual int Run(int argc, const char *argv[]) = 0;
virtual ~ISubProgram() {}
};
SubProgramList.hpp
#include <memory>
#include <boost/ptr_container/ptr_map.hpp>
#include <boost/noncopyable.hpp>
#include "ISubProgram.hpp"
class SubProgramList;
SubProgramList& GetSubProgramList();
class SubProgramList : boost::noncopyable
{
friend SubProgramList& GetSubProgramList();
SubProgramList() {} //Disallow direct creation.
boost::ptr_map<std::wstring,ISubProgram> programs;
public:
void RegisterProgram(std::auto_ptr<ISubProgram> subProgramToRegister);
ISubProgram * FindProgramFromExecutive(const std::wstring& executive);
void CallOnStartMethods(int argc, char *argv[]);
};
template <typename T>
struct RegisterSubProgram
{
RegisterSubProgram()
{
std::auto_ptr<ISubProgram> toAdd(new T);
GetSubProgramList().RegisterProgram(toAdd);
}
}
SubProgramList.cpp
SubProgramList& GetSubProgramList()
{
static SubProgramList theList;
return theList;
}
//Implementations of the class methods
ExampleSubProgram.cpp
#include "SubProgramList.hpp"
struct ExampleSubProgram : public ISubProgram
{
virtual std::wstring GetExecutive()
{
return L"ExampleSubProgram";
}
virtual int Run(int argc, const char *argv[])
{
//Run this program :)
}
};
namespace { RegisterSubProgram<ExampleSubProgram> registrar; }
Main.cpp
#include "SubProgramList.hpp"
int main(int argc, char *argv[])
{
SubProgramList& list = GetSubProgramList();
list.CallOnStartMethods(argc, argv);
std::wstring subProgramName(/*.. Generate from argv[1] ...*/);
FindProgramFromExecutive(subProgramName)->Run(argc, argv);
}
I think I'm clear of initialization order issues because the only global state is a local static rather than a global static.
The main reason for this is that I can completely pull apart the closed source and open source bits of the program, which would make merging quick and efficient, and also removes the boilerplate of my current "Giant if/else subprogram selector" in main.
Is this a reasonable use of on-start initialization (which is generally discouraged?) If not, what alternate implementation would you suggest?
I could come up with some clever programs with recursive template instantiation, but the reality is that what you've done is probably simpler than what I can come up with. It's rare that I suggest that global state is a smart idea, but on this one I may have to accept that I can't do better.

Valid OpenGL context

How and at what stage is a valid OpenGL context created in my code? I'm getting errors on even simple OpenGL code.
From the posts on comp.graphics.api.opengl, it seems like most newbies burn their hands on their first OpenGL program. In most cases, the error is caused due to OpenGL functions being called even before a valid OpenGL context is created. OpenGL is a state machine. Only after the machine has been started and humming in the ready state, can it be put to work.
Here is some simple code to create a valid OpenGL context:
#include <stdlib.h>
#include <GL/glut.h>
// Window attributes
static const unsigned int WIN_POS_X = 30;
static const unsigned int WIN_POS_Y = WIN_POS_X;
static const unsigned int WIN_WIDTH = 512;
static const unsigned int WIN_HEIGHT = WIN_WIDTH;
void glInit(int, char **);
int main(int argc, char * argv[])
{
// Initialize OpenGL
glInit(argc, argv);
// A valid OpenGL context has been created.
// You can call OpenGL functions from here on.
glutMainLoop();
return 0;
}
void glInit(int argc, char ** argv)
{
// Initialize GLUT
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE);
glutInitWindowPosition(WIN_POS_X, WIN_POS_Y);
glutInitWindowSize(WIN_WIDTH, WIN_HEIGHT);
glutCreateWindow("Hello OpenGL!");
return;
}
Note:
The call of interest here is glutCreateWindow(). It not only creates a window, but also creates an OpenGL context.
The window created with glutCreateWindow() is not visible until glutMainLoop() is called.