I recently started learning C++ and SDL2 because my ideas stretched beyond my skills. I'm not entirely new to programming, having done some R and python in the distant past. So, I'm entirely qualified to write a computer game from scratch in a language I'm only just learning...in my dreams.
I have two parallel code developments: the horribly long eye bleeding monstrosity that somehow works even as it balloons to a ridiculous size and the idyllic non-working game loop which I abstracted out in hopes of creating a central universal core engine to which I can tweak, add, and subtract the smaller individualized satellite programs it references for input, commands, graphics, etc. There is no actual game logic as yet. This is all universal elements and gui.
The trouble is, whenever I try to split my large block of code beyond one massive header file and one massive source file, I get long lists of errors involving missing or duplicate definitions. I resolve one error and my solution merely generates another. I haven't even touched the header. This was just from trying to divide that mammoth source file.
I know this is somewhat basic, but I'm having trouble wrapping my head around it. I'm including the idyllic broken version of my main and game headers and source files and my current satellite program headers to give an idea of what overlaps where. I wrote all of it except the functions in helper.h, to which I credit and profusely thank Will Usher. Also, I'm not sure whether to initialize the window and renderer in main.cpp, game.cpp, or graphics.cpp?
//main.cpp
void game_run();
int main(int arcg, char** arcv)
{
game_run();
return 0;
}
//game.h
#pragma once
/*
* game.h
*/
#ifndef GAME_H_
#define GAME_H_
while (gameState != GAME_EXIT)
{
//insert timestep code here later
while (SDL_PollEvent(&e))
{
switch (e.type)
{
case SDL_MOUSEMOTION:
mouse_x = e.motion.x;
mouse_y = e.motion.y;
//Cleanup Function
/*
* Recurse through the list of arguments to clean up, cleaning up
* the first one in the list each iteration.
*/
template<typename T, typename... Args>
void cleanup(T* t, Args&&... args) {
//Cleanup the first item in the list
cleanup(t);
//Recurse to clean up the remaining arguments
cleanup(std::forward<Args>(args)...);
}
/*
* These specializations serve to free the passed argument and also provide the
* base cases for the recursive call above, eg. when args is only a single item
* one of the specializations below will be called by
* cleanup(std::forward<Args>(args)...), ending the recursion
* We also make it safe to pass nullptrs to handle situations where we
* don't want to bother finding out which values failed to load (and thus are null)
* but rather just want to clean everything up and let cleanup sort it out
*/
template<>
inline void cleanup<SDL_Window>(SDL_Window* window) {
if (!window) {
return;
}
SDL_DestroyWindow(window);
}
template<>
inline void cleanup<SDL_Renderer>(SDL_Renderer* renderer) {
if (!renderer) {
return;
}
SDL_DestroyRenderer(renderer);
}
template<>
inline void cleanup<SDL_Texture>(SDL_Texture* texture) {
if (!texture) {
return;
}
SDL_DestroyTexture(texture);
}
template<>
inline void cleanup<SDL_Surface>(SDL_Surface* surface) {
if (!surface) {
return;
}
SDL_FreeSurface(surface);
}
I get long lists of errors involving missing or duplicate definitions
A function/member body can only be defined once within the solution (see exception below). If the linker finds that the body has been defined more than once, it emits an error re duplicate definitions.
This means that the code for function/member body should only be included as a compilation unit once. Note that using code guards/#pragma once doesn't fix this as these just work within the same compilation unit - not across compilation units.
If you really have to have multiple compilation units containing the same function/member body definition, then mark them as inline (comes before the return type). This means the linker will only try to link them once and not generate all the duplicate errors.