I'd like to keep my code compilable both on legacy C++ (C++ code using "NULL") and new C++11 standard (C++ code using "nullptr")
I'm using GCC, but planning to recompile the whole codebase also for VS when I'll finish most important things.
Should I expect both GCC and VS will do something like
#define NULL nullptr
Or Is better I'll do that myself (using of course a different name, where MY_LIB will be replaced by my library suffix)?
#ifndef nullptr
#define MY_LIB_NULL NULL
#else
#define MY_LIB_NULL nullptr
#endif
What I want to achieve is code that compiles regardless of wich C++11 features have been implemented or not (and since i'm not using templates, there are very few of them).
For example the keywords "override" and "final" are already done.
MY_LIB_OVERRIDE //macro, defines to "override" if c++11 is present.
MY_LIB_FINAL //macro, defines to "final" if c++11 is present.
I'm asking the question because I know the "nullptr" question is a bit strange, so maybe just doing the same I already did for override and final, is wrong. Needs opinions about that. Any help is wellcome.
You could probably create a "false" my_nullptr of type my_nullptr_t the following way:
const class my_nullptr_t
{
public:
/* Return 0 for any class pointer */
template<typename T>
operator T*() const
{
return 0;
}
/* Return 0 for any member pointer */
template<typename T, typename U>
operator T U::*() const
{
return 0;
}
/* Safe boolean conversion */
operator void*() const
{
return 0;
}
private:
/* Not allowed to get the address */
void operator&() const;
} my_nullptr = {};
This works with C++03 and C++11 and should always be safe, whichever C++11 features are implemented. That solution was actually already discussed in this topic that proposed a version of nullptr_t based on the Official proposal.
NULL is a macro that expands to a null pointer constant. It still works just like it used to. Code that has to work with non-C++11 compilers should use NULL.
I think following will works:
#include <cstddef>
#ifndef MY_LIB_NULL
#ifndef NULL //check for NULL
#define MY_LIB_NULL nullptr
#else
#define MY_LIB_NULL NULL ///use NULL if present
#endif
#endif
basically I check for "NULL". wich is a macro and can be checked, until the compiler is shipped with that macro (likely to be), than it's valid using the macro, when compiler will only provides "nullptr" and no longer have NULL then nullptr is used (maybe in a far future, but seems we can happily continue to use NULL!)
I think that's safer than redefining "nullptr" (like most people trying to do)
Related
In most cases, you can get away with assigning VK_NULL_HANDLE to any handle defined by VK_DEFINE_HANDLE(object) or VK_DEFINE_NON_DISPATCHABLE_HANDLE(object), but std::exchange() fails to set this value in almost all cases.
In most situations I have to call std::exchange(m_dispatchableHandle, nullptr) and std::exchange(m_nonDispatchableHandle, nullptr)
However, on x86, nullptr is invalid due to the redefinition of VK_DEFINE_NON_DISPATCHABLE_HANDLE. I must use std::exchange with NULL, 0, or (finally) VK_NULL_HANDLE.
My question: in following the VK_DEFINE_HANDLE macro patterns, should there not be two null handle macros defined like the following?
#define VK_NULL_HANDLE nullptr
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
#define VK_NON_DISPATCHABLE_NULL_HANDLE nullptr
#else
#define VK_NON_DISPATCHABLE_NULL_HANDLE 0
#endif
If nothing else, it seems like #define VK_NULL_HANDLE nullptr would have more utility in general.
I may be misunderstanding how the API is supposed to be used, so I'm sharing the trouble code in question: a move constructor that transfers handles and fills the old object with NULL or VK_NULL_HANDLE. By carefully managing ownership, the class can be designed simply and handle vkDestroy* et. al. in its destructor.
class RenderContext
{
public:
RenderContext(VkPhysicalDevice physicalDevice, uint32_t gfxQueueFamilyIdx, uint32_t presentQueueFamilyIdx);
RenderContext(const RenderContext& other) = delete;
RenderContext& operator=(const RenderContext& other) = delete;
RenderContext(RenderContext&& other) noexcept;
RenderContext&& operator=(RenderContext&& other) = delete;
~RenderContext();
private:
VkDevice _device;
VkCommandPool _gCmdPool;
VkCommandPool _pCmdPool;
VkQueue _gQueue;
VkQueue _pQueue;
};
RenderContext::RenderContext(VkPhysicalDevice physicalDevice, uint32_t gfxQueueFamilyIdx, uint32_t presentQueueFamilyIdx) :
_device(VK_NULL_HANDLE),
_gCmdPool(VK_NULL_HANDLE),
_pCmdPool(VK_NULL_HANDLE),
_gQueue(VK_NULL_HANDLE),
_pQueue(VK_NULL_HANDLE)
{
// ...
}
RenderContext::RenderContext(RenderContext&& other) noexcept :
_device(std::exchange(other._device, nullptr)),
_gCmdPool(std::exchange(other._gCmdPool, VK_NULL_HANDLE)),
_pCmdPool(std::exchange(other._pCmdPool, VK_NULL_HANDLE)),
_gQueue(std::exchange(other._gQueue, nullptr)),
_pQueue(std::exchange(other._pQueue, nullptr))
{}
RenderContext::~RenderContext()
{
vkDestroyCommandPool(_device, _pCmdPool, nullptr);
vkDestroyCommandPool(_device, _gCmdPool, nullptr);
vkDestroyDevice(_device, nullptr);
}
Templates in C++ require precise type, not just compatible one, to properly instantiate. As such C's NULL (defined as 0) might be a problem. 0 might also be a problem as it is int, not uint64_t, which is the ABI of Non-dispatchable Handles (which can be stored in 64 bit pointer to get some type safety out of C). This is not new when dealing with C libraries from C++ (or generally).
Rest is design questions and suggestions adressed at authors, and as such unsuitable for StackOverflow. The issue tracking system is available at https://github.com/KhronosGroup/Vulkan-Docs/issues. On the face, your suggestion makes sense to me. They probably do not want to make it pervasive, but additionally, it might be a case for doing #ifdef __cplusplus #define VK_NULL_HANDLE nullptr.
Alternatively, there is Vulkan-Hpp, which is supposed expose more C++-istic bindings to Vulkan.
Proper way currently to call the template function would be std::exchange(m_nonDispatchableHandle, decltype(m_nonDispatchableHandle)(VK_NULL_HANDLE));.
In most cases, VK_NULL_HANDLE can be assigned to any handle defined by VK_DEFINE_HANDLE(object) or VK_DEFINE_NON_DISPATCHABLE_HANDLE(object)
Wrong. You can get away with it because C and C++ let you. But the Vulkan specification is very clear:
The reserved values VK_NULL_HANDLE and NULL can be used in place of valid non-dispatchable handles and dispatchable handles, respectively
That is, VK_NULL_HANDLE is only for non-dispatchable handles. That the C/C++ languages allow you to assign the value of VK_NULL_HANDLE to a dispatchable handle is merely a function of the language, and the behavior for Vulkan itself is not defined.
You should never be trying to std::exchange between a dispatchable handle and a non-dispatchable handle, just as you should never be trying to std::exchange between two distinct types.
I have a C program which I need to connect to a C++ API. I asked on here and was given great advice, leading to creating a "wrapper".
So, in the API there is a type called "APIName::ReturnCode", and I wanted to create a C equivalent, so I've done the following:
In c_api.h:
#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif
typedef void* API_ReturnCode_t;
EXTERNC API_ReturnCode_t api_returncode_init();
EXTERNC void api_returncode_destroy(API_ReturnCode_t rc);
#undef EXTERNC
in c_api.cpp:
#include "c_api.h"
#include "/path/to/api/api.h"
API_ReturnCode_t api_returncode_init() {
return new APIName::ReturnCode;
}
void api_returncode_destroy(API_ReturnCode_t untyped_ptr) {
APIName::ReturnCode* typed_ptr = static_cast< APIName::ReturnCode*>(untyped_ptr);
delete typed_ptr;
}
So I compile that into a library and include it in my main program, and I can use things like:
API_ReturnCode rc;
to define a variable.
However, my next issue is how to define enumerated types in a similar way. So, the api has the following definition for error codes:
namespace APIName {
typedef enum ReturnCode_enum ReturnCode;
enum ReturnCode_enum {
RC_OK , // success
RC_ERROR , // general error
RC_NOT_AVAILABLE , // feature is not available
};
}
How do I recreate this in my wrapper so that I can do something like this in my code:
API_ReturnCode rc = API_RC_OK;
Thank you.
So after some clarification, my original answer is no longer applicable -- but is still retained below this answer.
Since the original C++ API cannot be altered in any way, you are much more limited in your available options.
You want to be able to do:
API_ReturnCode rc = API_RC_OK;
But rc is an opaque type (void*) that requires being destroyed with api_returncode_destroy -- so this won't be possible in an easy and sane way (not without confusing who owns the API_RC_OK calls). The biggest issue is that if we could produce an API_RC_OK instance, it leads to questionable ownership. For example:
API_ReturnCode rc = API_RC_OK;
api_returncode_destroy(rc); // is this good? is 'API_RC_OK' a resource that needs deleting?
And it gets more confusing in more complicated expressions.
Since the APIName::ReturnCode_enum type is just a classic C-style enum, which is implicitly convertible to an int, your best-bet here would be to try to preserve the int-like property by making API_ReturnCode_t's definition be:
typedef int API_ReturnCode_t;
Then any of the C++-wrapped calls can propagate the values as this int
Unfortunately to be able to receive these values on the other side, you will need to duplicate some effort here by manually re-creating these constants in some way. There are a few approaches that come to mind, all with pros and cons.
The inconvenient truth here is that, because you're trying to expose values defined in C++ in C, you'll need to somehow re-encode this on the other side in some way. You can't simply include the C++ header and use it in C, since they are different languages and C++ contains features that C doesn't understand.
1. Use extern constants
One possible approach is to use extern const values that get defined in the source from the underlying values, so you aren't stuck duplicating the values themselves. For example:
c_api.h
EXTERNC extern const API_ReturnCode_t API_RC_OK;
EXTERNC extern const API_ReturnCode_t API_RC_ERROR;
EXTERNC extern const API_ReturnCode_t API_RC_NOT_AVAILABLE;
c_api.cpp
extern "C" {
const API_ReturnCode_t API_RC_OK = APIName::RC_OK;
const API_ReturnCode_t API_RC_ERROR = APIName::RC_ERROR;
const API_ReturnCode_t API_RC_NOT_AVAILABLE = APIName::RC_NOT_AVAILABLE;
} // extern "C"
The good thing with this approach is that you aren't stuck manually setting API_RC_OK to 0, and API_RC_ERROR to 1, etc -- so these values are not strongly coupled.
The thing to watch out for is that these extern constants would not be (safely) usable from other objects during static initialization, since it's not guaranteed when these values will be set. If you aren't doing much static initialization, this shouldn't be of any concern.
2. Just duplicate the effort
If the enum is not large, and not likely to grow much larger, the obvious simple approach is to just do:
#define API_RC_OK 0
#define API_RC_ERROR 1
#define API_RC_NOT_AVAILABLE 2
or some equivalent thereof. The pro is that this can be used anywhere, compared to extern constants. The obvious con here is that the wrapper is strongly coupled to the wrapped library. If this is a large enumeration, or an enum that is likely to change often / regularly -- this is approach is probably not the best.
3. Define a possibly-orthogonal enumeration
One other option is to define an orthogonal enumeration instead. This requires re-defining the enum cases that you care about, and translating them through a separate function call. This results in more effort -- so depending on what you're doing, this may not be the best case.
c_api.h
typedef enum {
API_RC_OK,
API_RC_ERROR,
API_RC_NOT_AVAILABLE,
/* other states? */
} API_ReturnCode_t;
**c_api.cpp
API_ReturnCode_t to_return_code(APIName::ReturnCode rc)
{
switch (rc) {
case APIName::RC_OK: return API_RC_OK;
case APIName::RC_ERROR: return API_RC_ERROR;
case APIName::RC_NOT_AVAILABLE: return API_RC_NOT_AVAILABLE;
}
return API_RC_NOT_AVAILABLE;
}
In your wrapper code, anywhere you receive an APIName::ReturnCode you would now translate to an API_ReturnCode_t before returning back to the C caller.
The nice thing about this approach is that the enumerators no longer need to be in-sync, and that you can restrict the enum cases that you want to abstract out (assuming you don't want 1-1 mapping).
This also presents an easier way to upgrade in the future to different versions of the C++ library, since everything is internalized by the translation function. If the C++ library introduces new states, you can choose to coalesce some of those values together in a way that may make it more consumable by the C client.
The obvious downside with this approach is that it takes more work, since you're defining a separate hierarchy and a translation system that will be quite similar in the beginning. It's more work up-front for a higher return later on.
Old Answer
There is nothing specific to C++ about your ReturnCode_enum class. It's actually written in a more legacy-C++ style (e.g. not using enum class for scoping), which makes it usable in C directly.
So why not define the enum in the c_api.h header file instead, and use it in your C++ as well? This may require changing your opaque handle definition depending on what is stored in it; but this way you would have exactly 1 definition of the enumeration.
You can bring the C symbol into C++ namespaces using either typedef or using aliases, which allow a more C++-esque discovery of the values.
In c_api.h:
enum Api_ReturnCode_enum {
RC_OK , /* success */
RC_ERROR , /* general error */
RC_NOT_AVAILABLE , /* feature is not available */
};
/*
or 'typedef enum { ... } Api_ReturnCode_enum;' if you want don't want to specify
'enum' every time in C
*/
In your C++ API:
#include "c_api.h"
namespace APIName { // bring it into this namespace:
// Alias the "Api_" prefixed enum to be more C++ like
typedef Api_ReturnCode_enum ReturnCode;
// alternative, in C++11 or above:
// using ReturnCode = Api_ReturnCode_enum;
}
I wouldn't hide error code enums in opaque handles.
Create a new enum and convertion functions in the c_api.cpp file
c_api.h
typedef enum {
RC_OK,
RC_ERROR,
RC_NOT_AVAILABLE
} ReturnCode_copy;
ReturnCode_copy some_function(...);
c_api.cpp
static ReturnCode_copy convert(APIName::ReturnCode code) {
switch(code) {
//return correct ReturnCode_copy
}
}
ReturnCode_copy some_function(...) {
auto code = //some api function returning error code
return convert(code);
}
or you could be naughty and just copy the values directly in your new enum and just static_cast directly without the convert function.
In the Microsoft implementation of guidelines support library I see the following piece of code:
template<class T>
class not_null {
...
template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
constexpr explicit not_null(U&& u) : ptr_(std::forward<U>(u)) {
Expects(ptr_ != nullptr);
}
...
constexpr T get() const {
Ensures(ptr_);
return ptr_;
}
...
T ptr_;
}
All the constructors of gsl::not_null which take possibly pointers check these pointers are not null, but we still check stored value of pointer (ptr_) against null on each dereference. Why do we have this check, given that in C++ we typically don't pay for what we don't need?
UP: Ensures is implemented as follows (with default flags):
#define GSL_LIKELY(x) (!!(x))
...
#define GSL_CONTRACT_CHECK(type, cond) \
(GSL_LIKELY(cond) ? static_cast<void>(0) : gsl::details::terminate())
...
#define Ensures(cond) GSL_CONTRACT_CHECK("Postcondition", cond)
The comments are already giving the idea why removing null check from not_null::get() is not desirable. The main problem is that the change allows dereferencing a smart pointer after move.
For examples, see the following discussion on a PR that enables usage of not_null<unique_ptr> and how the change is incompatible with removing the null check from not_null::get()
https://github.com/Microsoft/GSL/pull/675
As for performance concerns, compiler optimizer should be able to remove many of the null checks, but not all, of course. If some checks are not removed but seem to be removable, we should fix compiler optimizations.
Premise:
I am trying to make a Define scope that is not implemented using a macro because of the potential issues with macros. Here is my initial attempt
//version for if not defined
bool Defined()
{
return false
}
//version for if defined
bool Defined(bool anything)
{
return true;
}
And an example use case
if(Defined(_DEBUG))
{
Stuff...
}
which would replace
#ifdef _DEBUG
Stuff...
#endif
or
#define Defined() false
#define Defined(Anything) true
Benefits:
syntax is cleaner, it is scoped,
This code is not conditional, so the compiler will be able to easily optimize code sections out.
Issues
There are a few issues with this procedure, the first is the reason for this post.
Question:
You can't pass in anything that is not implicitly cast-able to a bool. Is there a way to implicitly cast any object, number, pointer, etc to a bool? I don't believe there is, but I wanted to make sure, before I continued.
You can use a generic template:
template<class T>
bool Defined(T &&) { return true; }
I get a whole lot of warnings about switches that only partially covers the range of an enumeration switched over. Therefor, I would like to have a "default" for all those switches and put __builtin_unreachable (GCC builtin) in that case, so that the compiler know that case is not reachable.
However, I came to know that GCC4.3 does not support that builtin yet. Is there any good way to emulate that functionality? I thought about dereferencing a null pointer instead, but that may have other undesirable effects/warnings and such. Do you have any better idea?
The upcoming 2023 revision of the C standard (C23, ISO/IEC 9899:2023) is going to have a new macro unreachable
#include <stddef.h>
void unreachable(void);
with the effect of gcc's __builtin_unreachable.
On older C standards, you may be able to call an inline function declared _Noreturn to mark anything after that call as unreachable. The compiler is allowed to throw out any code after such a function. If the function itself is static (and does return), the compiler will usually also inline the function. Here is an example:
static _Noreturn void unreachable() {
return; /* intentional */
}
/* ... */
foo();
bar(); /* should better not return */
unreachable();
baz(); /* compiler will know this is not reachable */
Notice that you invoke undefined behavior if a function marked _Noreturn indeed returns. Be sure that said function will never be called.
Hmm, something like (since __builtin_unreachable() appeared in 4.5):
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#if GCC_VERSION >= 40500
#define my_unreachable() __builtin_unreachable()
#else
#define my_unreachable() do { printf("Oh noes!!!111\n"); abort(); } while(0)
#endif
Would abort (leaving a core dump) or throw (allowing for alternate data capture) accommodate your needs?
Do you really want to have switch statements that don't cover the full enumeration? I nearly always try to list all the possible cases (to no-op) with no default case so that gcc will warn me if new enumerations are added, as it may be required to handle them rather than letting it silently (during compile) fall into the default.
keep it simple:
assert(false);
or, better yet:
#define UNREACHABLE (!"Unreachable code executed!")
assert(UNREACHABLE);
template<unsigned int LINE> class Unreachable_At_Line {};
#define __builtin_unreachable() throw Unreachable_At_Line<__LINE__>()
Edit:
Since you want to have unreachable code to be omitted by compiler, below is the simplest way.
#define __builtin_unreachable() { struct X {X& operator=(const X&); } x; x=x; }
Compiler optimizes away x = x; instruction especially when it's unreachable. Here is the usage:
int foo (int i)
{
switch(i)
{
case 0: return 0;
case 1: return 1;
default: return -1;
}
__builtin_unreachable(); // never executed; so compiler optimizes away
}
If you put __builtin_unreachable() in the beginning of foo() then compiler generates a linker error for unimplemented operator =. I ran these tests in gcc 3.4.6 (64-bit).