TIL: C++ character constants aren’t created equal

arrPtrTerm

When reviewing a patchset involving files, strings, timers, and multiple threads (essentially all the “tough to review” checkboxes right there), a comment from :froydnj caught my eye:

> +const char* kPersistenceFileName = "gv_measurements.json";
> +const char* kPersistenceFileNameNoExt = "gv_measurements";

Please make these `const char kPersistence...[] = "..."`, which is slightly smaller on all of our usual systems.

Why? Aren’t C arrays just pointers to the first element? Don’t we treat char[] and char* identically when logging or appending them to higher-order string objects?

Influenced by :Dexter and :jan-erik, I tried this out myself. I made two small C programs arr.c and ptr.c where the only difference was that one had a const char* and the other a const char[].

I compiled them both with gcc and then checked their sizes in bytes.arrPtrSizes

Sure enough, though ptr.c was smaller by one byte (* is one character and [] is two), after being compiled ptr was larger by a whole 8 bytes!

This is because C arrays aren’t identical to pointers to the first element. Instead they are identifiers that are (most of the time) implicitly converted to be pointers to the first element. For instance, if you’re calling a function void foo(char c[]), inside the function `c` is implicitly a char* and its size is that of a pointer, but outside of the function c could be an array with its size being the length of the array. As an example:

void foo(char c[]) {
  printf("sizeof(c): %d\n", sizeof c);
}
int main(void) {
  char arr[] = "a";
  printf("sizeof(arr): %d\n", sizeof arr);
  return 0;
}

Prints:

sizeof(arr): 2
sizeof(c): 8

Another way to think about this is that the char* form allocates two things: the actual string of four (“abc” plus “\0”) characters, and a pointer called ptr that contains the address of the place the ‘a’ character is stored.

This is in contrast to the char[] form which allocates just the four-character string and allows you to use the name arr to refer to the place the ‘a’ character is stored.

So, in conclusion, if you want to save yourself a pointer’s width on your constant strings, you may wish to declare them as char[] instead of char*.

:chutten

(( :glandium wrote a much deeper look at this problem if you’d like to see how much worse this can actually get thanks to relocations and symbol exporting ))

(( :bsmedberg wrote in to mention that you almost never want const char* for a constant anyway as the pointer isn’t const, only the data is. ))

Advertisements

TIL: Feature Detection in Windows using GetProcAddress

In JavaScript, if you want to use a function that was introduced only in certain versions of browsers, you use Feature Detection. For example, you can ask “Hey, browser, do you have a function called `includes` on Array?” If the browser has it, you use it; and if it doesn’t, you either get along without it or load your own implementation.

It turns out that this same concept can be (and, in Firefox, is) done with Windows APIs.

Firefox for Windows is built against the Windows 10 SDK. This means the compiler knows the API calls and type definitions for all sorts of wondrous modern features like toast notifications and enumerating graphics adapters in a specific order.

However, as of writing, Firefox for Windows supports Windows 7 and up. What would happen if Firefox tried to use those fancy new Windows 10 features when running on Windows 7?

Well, at compile time (when Mozilla builds Firefox), it knows everything it needs to about the sizes and names of things used in the new features thanks to the SDK. At runtime (when a user runs Firefox), it needs to ask Windows at what address exactly all of those fancy new features live so that it can use them.

If Firefox can’t find a feature it expects to be there, it won’t start. We want Firefox to start, though, and we want to use the new features when available. So how do we both use the new feature (if it’s there) and not (if it’s not)?

Windows provides an API called GetProcAddress that allows the running program to perform some Feature Detection. It is asking Windows “Hey, so I’m looking for the address of this fancy new feature named FancyNewAPI. Do you know where that is?”. Windows will either reply “No, sorry” at which point you work around it, or “Yes, it’s over at address X” at which point to convert address X into a function pointer that takes the same number and types of arguments that the documentation said it takes and then instruct your program to jump into it and start executing.

We use this in Firefox to detect gamepad input modules, cancelable synchronous IO, display density measurements, and a whole bunch of graphics and media acceleration stuff.

And today (well, yesterday at this point) I learned about it. And now so have you.

:chutten

–edited to remove incorrect note that GetProcAddress started in WinXP– :aklotz noted that GetProcAddress has been around since ancient times, MSDN just periodically updates its “Minimum Supported Release” fields to drop older versions.

C++: Today I Learned How to Use dtor Order to Detect Temporaries

On Friday I wrote the kind of C++ bug you usually write on Fridays: a stupid one. I was trying to create an object that would live exactly until the end of the block using RAII (as you do). This is how I wrote it:

RAII(parameters);

What I should have written was:

RAII someRAII(parameters);

Because, according to 12.2/3 of the ISO C++ standard, the temporary object created by the RAII construction in the first case will only last until the end of its containing expression.Whereas in the second case the temporary is assigned a reference `someRAII` and its lifetime is thus lengthened to the lifetime of the reference.

As I had it written, the RAII would last until the semicolon. Which isn’t very long at all for something I was supposed to be using to mark a stack frame’s duration.

There should be a law against this! I thought. Why does the compiler even have that lever?

Or, more seriously, how can I stop this from happening to me again? Or to others?

This being Gecko I’m hacking on, :froydnj informed me that, indeed, there are two different ways of catching this blunder. Both happen to be documented on the same page of the MDN about how to use RAII classes in Mozilla.

The first way is adding custom type annotations to mark the class as non-temporary, then having a clang plugin throw during static analysis if any scope has a “non-temporary”-marked class being allocated as a temporary.

(( #include “mfbt/Annotations.h” and add MOZ_RAII to your class decl to use it.))

That only works if you’re on a platform that supports clang and have static analysis turned on. This wouldn’t help me, as I’m developing Gecko on Windows (Why? A post for another time).

This brings us to the second, cross-platform way which is unfortunately only a runtime error. As a runtime error it incurs a runtime cost in CPU and memory (so it’s only compiled on debug builds) and it requires that the code actually run for the test to fail (which means you might still miss it).

This second way is a triplet of macros that annotates an RAII class to have the superpower to detect, at runtime, whether or not an instance of that class being destructed was allocated as a temporary or not.

Sound like magic? It all comes down to what order destructors are called. Take two classes, A and B such that A’s ctor takes a temporary B as a default arg:

A(B b = B())

Try allocating an instance of A on the stack and watch what order the ctors/dtors are called:

A a;
B()
A()
~B()
~A()

Allocate an A as a temporary and you get something different:

A();
B()
A()
~A()
~B()

(Here’s a full example at ideone.com so you can run and tweak it yourself)

They both start the same: you need to create a B instance to create an A instance, so b’s ctor goes first, then a’s.

b is a temporary, so what happens next is up to section 12.2 of the standard again. It lasts until the semicolon of the call to the A ctor. So in the stack case, we hit the semicolon first (~B()) then the stack frame holding a is popped (~A()).

When a is also a temporary, it gets interesting. Who goes first when there are two temporaries that need to be destructed at the same semicolon? Back to 12.2, this time to footnote 8 where it says that we go in reverse order of construction. So, since we call B() then A(), when we hit the semicolon we roll it back as ~A() then ~B().

So if ~A() happens before ~B(), then you were a temporary. If not, you weren’t. If you tell A when B is destructed, when A goes away it’ll know if it had been allocated as a temporary or not.

And, of course, this is exactly how those macros grant superpowers:

  1. MOZ_GUARD_OBJECT_PARAM puts the temporary B b = B() in your “A” class’ ctor args,
  2. MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER puts a little storage on your “A” for B to use to notify your “A” when it’s been destructed.
  3. MOZ_GUARD_OBJECT_INIT tells B how to find your “A”

(( It’s all in GuardObjects.h ))

It takes what is a gotcha moment (wait, the destruction order is different when you allocate as a temporary?!) and turns it into a runtime test.

Ah, C++.

:chutten