Followers 0

# My special blend of C and C++...

## 10 posts in this topic

Ah yes, the C–with–classes phase

It's a phase?  Damn, I've been going through that phase for about 20 years then. ;)

2

##### Share on other sites

Looks like what a 90s Java programmer would make if told they had to get something working in C++ in a day. Everything 'new'ed, everything gettered-and-settered.

2

##### Share on other sites

Looks like what a 90s Java programmer would make if told they had to get something working in C++ in a day. Everything 'new'ed, everything gettered-and-settered.

I was just getting my feet wet after years of resistance to C++ and OOP. I'm still resistant to OOP, but slowly coming around. I didn't understand that the purpose of the containers was to remove the need for new/delete and it never occurred to me that if I'm exposing the member to the user via get/set, then it probably needs to be public.
1

##### Share on other sites
it never occurred to me that if I'm exposing the member to the user via get/set, then it probably needs to be public.

It actually depends on the nature of the class.

For classes whose single responsibility is to contain data, you are right that they should generally just have public data fields.  Mutators and accessors don't make sense for these.

For classes whose single responsibility is actions, the functions should typically be actions.  They should be verbs, DO something.  In the rare case where the inner guts of the object needs to be exposed -- and it should be rare in well-written code -- then proper accessor and mutator methods are the right choice.  They generally indicate that you probably shouldn't be mucking about with the object internals.

2

##### Share on other sites

I actually wrote a version of SAFE_DELETE myself in one project, a long, long time ago. That was to fix a crash, and successfully fix the crash it did!

It didn't occur to me at that time that when you need to set pointers to nullptr after deleting, then your program logic is totally fucked. It didn't occur to me that double deletes crashing hard was actually a good thing, either.

EDIT: <loosely related c++ rant>
By the way, the C++ committee has put a lot of effort to make writing code that crashes in a meaningful way (with constants as the ones in the previous post) as hard as possible with C++14.

I noticed first when writing a cross-platform lib which uses among other things... well, "operating system handles" of some kind, which are void pointers under Windows. There are special values that designate special intentions or conditions. One such special value is -1 cast to the respective type. Now, -1 is a perfectly good compiletime constant, is it not. Just like 0xdeadbeef would be. Thus, the obvious thing is something like:

constexpr handle_t invalid_handle_value = reinterpret_cast<handle_t>(-1);


Guess what the compiler tells you (regardless of what type of cast you attempt):

constexpr variable 'invalid_handle_value' must be initialized by a constant expression
note: reinterpret_cast is not allowed in a constant expression

Great. Thank you. First I thought it was a compiler bug, but this error indeed follows exactly the explicit wording of the standard.

So, we're back to #define invalid_handle_value ((void*)-1). Or we can use a normal const variable, for which the compiler will needlessly allocate an address and room in the executable's rdata section. For a compiletime constant. Very intelligent, thank you again. Much better now.

Guess what the compiler tells you when you try something like constexpr handle_t invalid_handle_value =  &((char*)nullptr)[-1]; to work around this :)

Apart from obviously invoking undefined behavior, this is not a constant expression either...
</loosely related c++ rant>

Or, since you don't actually need the constexpr, you could just write a small inline function:

inline const handle_t invalid_handle() { return reinterpret_cast<handle_t>(-1); }


It will be inlined by just about every compiler out there on the lowest (non-disabled) optimization settings, so no additional global will be allocated. Many of the constants in std::numeric_limits are actually functions.

1

##### Share on other sites

EDIT: Ok, I just read that constexpr wasn't possible in that case. I'll leave my answer on as it still a valid point towards l0calh05t reply

Or, since you don't actually need the constexpr, you could just write a small inline function: inline const handle_t invalid_handle() { return reinterpret_cast(-1); } It will be inlined by just about every compiler out there on the lowest (non-disabled) optimization settings, so no additional global will be allocated. Many of the constants in std::numeric_limits are actually functions.

1) A constexpr variable will be fully evaluated at compile-time, so there should be no real difference between a constexpr function and a variable.

2) Therefore, if you actually replace a constexpr-variable, do it with a constexpr function. While the compiler can inline your function, constexpr ensures that it will. Actually a constexpr-function will be resolved to a value at compile-time, so this can be enormously faster than even an inlined function (since no code has to be executed for the function at all).

;TL;DR: constexpr > compiler inlining (if you can actually use it :D )

Great. Thank you. First I thought it was a compiler bug, but this error indeed follows exactly the explicit wording of the standard.

Yeah, unfortunately if the type of a constexpr-statement is not a primitive type, you are out of luck. You can define a constexpr-ctor, but I most (platform)-libraries actually won't do that.

Out of interest, whats the exact type of handle_t in your example?

Edited by Juliean
1

##### Share on other sites

EDIT: Ok, I just read that constexpr wasn't possible in that case. I'll leave my answer on as it still a valid point towards l0calh05t reply

Or, since you don't actually need the constexpr, you could just write a small inline function: inline const handle_t invalid_handle() { return reinterpret_cast(-1); } It will be inlined by just about every compiler out there on the lowest (non-disabled) optimization settings, so no additional global will be allocated. Many of the constants in std::numeric_limits are actually functions.

1) A constexpr variable will be fully evaluated at compile-time, so there should be no real difference between a constexpr function and a variable.

2) Therefore, if you actually replace a constexpr-variable, do it with a constexpr function. While the compiler can inline your function, constexpr ensures that it will. Actually a constexpr-function will be resolved to a value at compile-time, so this can be enormously faster than even an inlined function (since no code has to be executed for the function at all).

;TL;DR: constexpr > compiler inlining (if you can actually use it :D )

Which you can't in this case, ergo const function provided inline. Also, constepxr doesn't guarantee inlining either BTW. Unless evaluated into a constexpr "variable".

1

##### Share on other sites

Also, constepxr doesn't guarantee inlining either BTW. Unless evaluated into a constexpr "variable".

True, yet its easier to control/check, and a constexpr-function with zero-parameters that is once checked to produce a constexpr-output will AFAIK always be "inlined". In this case its not important though since it cannot be applied - I'm still curious whats the actual type of handle_t is, and if there is not a way to enforce a variable with constexpr nevertheless.

1

## Create an account

Register a new account