Not exact related, but is there a modern, widely used object oriented c++ wrapper for OpenGL? Why does OpenGL persist with a C style API instead of a more expressive one?
There's no wrapper because a modern high level rendering engine doesn't expose raw graphics APIs to the user, and instead keeps it behind higher level structures like draw calls and render graphs. At that layer, you don't need the C++ API, in fact you want something to efficiently let you manage the OpenGL context state as directly as possible. And a C API is fine for that.
Basically, the amount of code interfacing with OpenGL should be fairly minimal.
There are higher level graphics libraries on top of OpenGL, Vulkan, Metal, WebGL & DirectX, for example bgfx or sokol, but IIRC they still go C style for the same reason. Then there are language-specific libraries that will be idiomatic, like wgpu for Rust. Finally, you reach entire very opinionated game engines at various levels (Unity, UE, Godot, etc).
While not being based on C DirectX actually has a C API, even for D3D12. For example to call device->CreateRootSignature(...) from C you call ID3D12Device_CreateRootSignature(device, ...).
Before or after dealing with the dynamic loading of all required extensions, validation layers, memory allocator (which AMD had to create a more user friendly wrapper) and initialization boilerplater?
I disagree; type safety is something that is missing from almost every C api I've used; you end up with opaque handles that are just integers that can be easily misused. Consider the glBufferSubData method [0] - 2 of 4 of those parameters are really just integers that the programmer needs to know about, that the compiler won't check,and the last two are tightly coupled and not type safe; thetes nothing stopping you from passing an array of doubles (or whatever pointer you have lying around).
Opengl also gets away with because you're almost always working with either floats or integers (for say index buffers) but once you start dealing with containers of containers, a C api ends up really messy with everything being an "object" and having to call function_str(my_obj_handle) with the correct handle - see sentry's api [1] for an example of what this looks like.
A C++ api for OpenGL would allow for type safe flags, type and bounds checked buffers, RAII handles with all the good (and granted, bad) that comes with them.
It seems like you don’t consider requirements like ABI at all. There’s a reason for opaque handles (typeless pointers or integer handles).
When managing resources on separate memory, RAII is also rarely something you’d want. There are different memory management strategies and tight control over memory is very important.
Even if you implement your library in C++ you will need to expose a C API to be compatible with other languages. Not everyone uses C++.
> When managing resources on separate memory, RAII is also rarely something you’d want.
It's the opposite.
When managing resources in system memory, RAII is rarely something I’d want because malloc() is slow.
When managing resources on separate device, RAII is invaluable. These external resources often have API designed like bind/use/unbind, map/update/unmap and similar. You forget that one close/unbind/unmap and you violate your part of the contract i.e. anything may happen including crashes and memory leaks.
DirectX is only barely C++. It's structs and virtual interfaces. They actually provide a full C interface. The API itself is exceptionally C-like IMHO. It does not, for example, return unique_ptr or vectors. There's no std::string or std::function. In fact there's no std:: anything.
Don't get me wrong I like namespaces, constructors, and methods over C-style "namespace", no constructors, and an ocean of loose functions. If people want to create and use C++ wrappers around C APIs that's great. But OP's question was "Why does OpenGL persist with a C style API instead of a more expressive one?".
Writing a C API and wrapping with C++ is very different than writing an "expressive" C++ interface imho.
A C++ wrapper doesn't need std::anything to be C++.
Barely C++ is still an improvement over bare bones C.
LibGNM(X) and GX2 are also not std::whatever_else, but again build up on not being a bare bones C API stuck in the days of IrisGL.
Finally Metal is a mix of Objective-C and C++, both definitly an improvment.
Then there is the whole issue of they are proper frameworks, not "here is a specification and now go hunting how to load fonts, models, materials" that Khronos does.
Agreed 100%. I quite like C APIs. They’re simple, elegant, and can wrapped with RAII or your preferred style.
A C++ API imposes its style on you. Usually badly. Never mind the ABI issues.
Most interfaces could be improved by re-writing them in C. I can’t say the same for the inverse.
That said, OpenGL is a terrible API that is nearly impossible to use correctly. But neither C nor C++ have much impact on its horrifically stateful architecture.
C++ APIs are made far better by templates (and soon concepts), smart pointers, objects, better type checking, etc, not just RAII. C-style APIs really can't compete, IMO.
A C API can called by every language under the sun. C is the lingua franca of computer science.
A C++ API can't even be reliably called by other C++ programs. If I produce a shared library with C API it can be used broadly and easily. If I produce a shared library with a C++ it can only be used by C++ programs compiled with precisely the same settings.
Providing a C++ API really just means "compile this entire library from source". I do indeed compile a lot of great third-party C++ code from source. That's not always possible or even desirable.
If I need to use that code from a different language - for example C# for Unity or matlab for data science - then that C++ API is worthless and needs to be wrapped with a C API.
I've recently been integrating some C++ libraries into C++ based Unreal Engine. It's easier to integrate a C API with Unreal C++ than it is to integrate random C++ project with C++ based Unreal.
The fact that C++ doesn't have a standard build system doesn't help. Integrating a C++ library to compile from source requires adapting random build system A with your build system B. There's a reason C++ projects love single-header (or single header + single cpp) C++ libraries with no additional dependencies. Because they don't require jumping through a million hoops.
> A C API can called by every language under the sun.
And is usually only used as a last resort. I've lost count of the number of libraries reimplemented in different languages just to avoid using a C-style API at all.
> C is the lingua franca of computer science.
Is it? If you were to randomly draw a developer from the body of working programmers, how confident would you really be that they could competently write a good portable C program, or even write one at all? I certainly wouldn't put any money on it.
C++ is the preferred language of web browsers, interpreters, games/game engines, GUIs, heterogeneous programming frameworks, and every relevant C compiler.
And to reiterate, a C ABI is inherently more fragile than a C++ ABI simply due to the lack of name mangling, among other things. The fact that platforms choose to never touch their libc doesn't change that. But if you have to maintain a C library you will feel this.
> Is it? If you were to randomly draw a developer from the body of working programmers, how confident would you really be that they could competently write a good portable C program, or even write one at all? I certainly wouldn't put any money on it.
Perhaps what they meant is more like "C is the lingua franca of linking". CS people or developers may not be great at writing C code, but almost everything can execute it. C++ of course, but also Java and Python. I think even Haskell can call C code, but I'm not certain of that.
As a C programmer I find it really telling when an API is full of smart pointers. It means the heap is completely unmanaged and I have no control over memory allocation. It makes most c++ libraries completely unusable in embedded, high performance, and high reliability environments (all of which don’t randomly allocate memory at random sizes throughout their run time).
This is why I really, really like unique_ptr. You either explicitly take responsibility (and it implicitly gets handled) or you explicitly hand it off.
All platform APIs are in C and not C++. The main reason IMO is the lack of standardization of C++ ABI. This is not such an obvious problem if you always only use single compiler, but the main difference between C and C++ after all the language sugar has been removed is that C++ binaries on a single platform, compiled on two different compilers beyond their C like aspects are only accidentally compatible.
On some platforms you can make even single compiler builds in C++ incompatible if you use stl - e.g. a stl::vector may be quite different in debug and release build which leads naturally to faulty memory acceess.
There's https://openframeworks.cc/. It's basically the C++ equivalent to https://processing.org/. Unfortunately, it's not really a library but rather a complete app framework, but it shows how a C++ wrapper API could look like. It definitely makes OpenGL programming easier and more fun.
Ogre is the renderer for a number of robotics projects, like rviz (robot visualization) and Gazebo (robot simulation). I don't know that either of those necessarily showcase it in the best possible light, but more just to say that it has a rich history serving in a variety of cross-platform use cases.