1

Here is a snippet of the code I have:

class modbus {
public:
  static const uint8_t modbusHeader = 2;
  static const uint8_t modbusCRC = 2;
  static const uint8_t modbusPDU = modbusHeader + modbusCRC;
  static const uint8_t exceptionBase = 0x80;
  static const uint32_t transmitTimeout = 5000;
};

It defines some sizes for the modbus packets that I need to create inside the class. I work inside an embedded environment and as such size optimisations and considerations are always there. As such I really want to have only one occurrence of these constant values inside my read only part of the flash.

I have chosen to set these variables as static but is this necessary? Would a compiler infer that these values need only be saved once inside the binary and as such only include them once when I remove the static keyword?

Tarick Welling
  • 3,119
  • 3
  • 19
  • 44
  • if you remove the `static` the semantics gets changed. – L. F. Sep 16 '19 at 10:31
  • True but I feel that it is unneeded as it isn't a mutable variable and such doesn't convey a meaning of multiple objects accessing a single variable. I think it will remove some noise if I remove the `static` as there is no chance for race conditions and the like. But I'm concerned about about space optimizations. – Tarick Welling Sep 16 '19 at 10:35
  • Why would there be race conditions if you only ever read from the variables? What system is this? You mention flash. Is it a microcontroller or some PC in disguise? – Lundin Sep 16 '19 at 11:18
  • Your question is missing some key information. If you remove the `static` keyword, the language calls for each object of the class to have its own copy of the constants (possibly different values per object). How would a compiler infer that these values need only be saved once? – JaMiT Sep 16 '19 at 11:23
  • Possibly related: https://stackoverflow.com/questions/3709207/c-semantics-of-static-const-vs-const – Clifford Sep 16 '19 at 19:20

3 Answers3

5

I suppose, technically, if the compiler knew that you never performed sizeof on modbus, and never took the address of these members through different modbus* pointers, and knew that they were only ever initialised with the exact same trivial value, it might use the "as-if" rule to merge them into one and remove them from the class in terms of storage. (If it couldn't guarantee just one of these things, the rules of the language would be violated.)

But that's a tall order (particularly when you consider multiple translation units), and would not really be useful.

So no. I don't expect that this would ever happen.

You should indeed make those things static const (with perhaps a sprinkling of constexpr).

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
0

The code

#include <cstdint>

class modbus {
public:
  static const uint8_t modbusHeader = 2;
};

int main() {
    return modbus::modbusHeader;
}

results in

main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $2, %eax
        popq    %rbp
        ret

and

#include <cstdint>

class modbus {
public:
  const uint8_t modbusHeader = 2;
};

int main() {
    return modbus().modbusHeader;
}

results in

modbus::modbus() [base object constructor]:
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     BYTE PTR [rax], 2
        nop
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     BYTE PTR [rbp-1], 0
        lea     rax, [rbp-1]
        mov     rdi, rax
        call    modbus::modbus() [complete object constructor]
        movzx   eax, BYTE PTR [rbp-1]
        movzx   eax, al
        leave
        ret

At least here is a large difference. I used g++ 9.2 with -O0

Thomas Sablik
  • 16,127
  • 7
  • 34
  • 62
  • It gets interesting when you try to do (and use) `modbus::modbusHeader` (in the `static`-class-member version where modbusHeader is "initialized" inside the class definition). The compiler (g++ and clang) lets you do that without complaint but it fails to link. It only links when you initialize the static-class member outside the class definition. I wonder if it's broken compilers or a broken language spec. – Petr Skocik Sep 16 '19 at 10:55
  • It's a flash-based embedded system, not an x86 PC. Disassembly is completely irrelevant, linking is what matters here. – Lundin Sep 16 '19 at 11:03
  • `-O0` is helpful in debugging, but harmful when describing actual system behavior. – MSalters Sep 16 '19 at 11:13
0

If you insist on using C++, you must const-qualify an object of the class, not members of the class. You must then ensure that const variables with static storage duration end up in flash for your given target. Often the tool chain has a build called "flash-something". It's related to the linker rather than the compiler. If that part is working, it shouldn't matter if the object of the class is static or not, as long as the object is declared at file scope (static storage duration).

What you shouldn't do, is to just const qualify class members, rather than the object they reside in. Because that might cause silent C++ bloat to remain in the executable, in the form of default constructors called by the "CRT". You can verify this by single-stepping through the part of the CRT that deals with calling constructors of objects with static storage duration.

As for making members static, you should only need to do that for purposes like singleton, not for making members end up in flash.

Lundin
  • 195,001
  • 40
  • 254
  • 396