I am wondering how libraries create graphics contexts in a cross-platform manner. Somehow libraries like SDL create graphical windows seemingly without using the system headers. I know how to set up a window for graphics using Win32API, and how to get input from it using the same. How do I write a program such that it can be executed on any system (linux in particular) without requiring a windows environment? Or would I have to create two versions of my program, one for windows and one for linux?
The way this is done is quite simple. It's all a matter of creating an interface (i.e., set of functions or classes) that is not specific to any particular platform. Then, you create an implementation that fulfills that interface for each platform you want to support in a way that hides all the platform-specific code. This way, you need to compile your library for each platform, but for the user of the library, it all appears to be the same because the user only interacts with the interface (not with the encapsulated implementation details).
If you take the example of SDL. The Unix/Linux/Mac API for creating and dealing with a window is very different from the Win32 API used in Windows. However, in abstract terms, they do the same things, e.g., create a window, set some pixel values on it, have "redraw" events, etc... So, the SDL library has created a set of abstractions, like SDL "surfaces", that encapsulate those essential and common functionalities between the platforms. So, as a user of SDL, you only have to deal with those, and you can remain oblivious to the implementation details (i.e., what actual API calls are done under-the-hood). Then, the SDL implementers simply create source files (.c, .cpp files) for each platform, and that use the correct API calls to fulfill the "contract" or functionality that is exposed by the platform-agnostic interface.
This is really the key to creating a cross-platform library that requires the use of platform-specific API calls. You hide away all the platform-specific code behind platform-agnostic interfaces, so the user only has to deal with one interface for all target platforms.
Ok, just to check my understanding, you're saying that I cannot create 1 program that will be cross platform (in c++) but I can make a library that is cross platform by swapping in correct code at compile time (presumably using #ifdef directives). Then if I want my code to work on two systems I compile it twice, once with each OS specified?
you're saying that I cannot create 1 program that will be cross platform (in c++) but I can make a library that is cross platform
Whether you create a program or a library, the situation is the same. Usually, it is more practical to make a cross-platform library (i.e., encapsulate the platform-specific code in a library). However, you could also do it in a program.
by swapping in correct code at compile time (presumably using #ifdef directives).
Yes. You swap the correct code at compile time, either using some #ifdef stuff in the code, or using the build script to swap between distinct cpp files that implement the same functions but in a different way (for each platform).
Then if I want my code to work on two systems I compile it twice, once with each OS specified?
Yes. This is often inevitable, depending on how different the platforms are. Some platforms use completely different binary formats for executables and libraries. Specifically, Windows versus Linux/Unix/BSD (i.e., POSIX). Among different versions of Linux and other similar systems, things will mostly work without recompilation, as long as the target architecture (instruction set) is general enough (like i386 for example). But executable programs for very different platforms like Mac, Linux and Windows are not going to work between them, you have to recompile for each platform.
When we talk about doing cross-platform development in C/C++ (or any other native compiled language), we are just talking about the fact that the same source code can compile (unmodified) for the different platforms without any issue. But at the end of the day, if you want to distribute binaries (executables / compiled libraries), you still need to compile the code for each platform (usually, Mac / Linux / Windows, and for both 32bit and 64bit if you want to take advantage of the 64bit instructions when available), and distribute each version. As an example, see the distribution for CodeBlocks, they have different binary distributions, but only one set of source code for all, this is very typical of cross-platform projects.
And on a practical note... Although somewhat unrelated to the actual question, for 3D game development in particular, or 2D a lot of the actual physics/collisions etc has already been nicely coded in the much used open source blender program.
This is by the far the easiest to make actual good looking 3D games without having to worry abou the physics. And it works across all OS.