0

I am a newbie in C++ and I have encountered a problem that I need to define a new data types (unions concretely) via typedef in my class. The relevant code snippet of .h module of my class is following

class Manager
{

  public:

    static const uint8_t NO_BYTES_IN_PACKET;
    static const uint8_t NO_PYLD_BYTES_IN_CONTROL_PACKET;

    // control packet structure
    typedef union{
        struct{
            uint8_t header[3];                                
            uint8_t payload[NO_PYLD_BYTES_IN_CONTROL_PACKET];  
        }pkt_parts_t;
        uint8_t pkt_array[NO_BYTES_IN_PACKET];
    }control_pkt_u;

  private:

}

My problem is that the constants

static const uint8_t NO_BYTES_IN_PACKET;
static const uint8_t NO_PYLD_BYTES_IN_CONTROL_PACKET;

are defined in associated .cpp module

const uint8_t Manager::NO_BYTES_IN_PACKET = 8;
const uint8_t Manager::NO_PYLD_BYTES_IN_CONTROL_PACKET = 5;

Due to that I have been receiving an error message: error: array bound is not an integer constant before ']' token during compilation process. My idea was to move the union definition into the .cpp module but I am not sure whether it is correct approach. What are your opinions? Thank you for any ideas.

Steve
  • 805
  • 7
  • 27
  • 2
    aside: `typedef union` is a very C thing to do... – UKMonkey Feb 26 '18 at 16:10
  • 1
    And in that very C thing, `const uint8_t foo = 5;` does not qualify as a constant for purposes of array declaration. (e.g.`#define NO_BYTES_IN_PACKET 8`) – David C. Rankin Feb 26 '18 at 16:12
  • Array size needs to be known at compile time. You need to either use `#define` to set the array size or write the number literal into the braces. The runtime eavaluation of your const values does not work. – Nefrin Feb 26 '18 at 16:12

2 Answers2

2

If you don't odr-use those static const int, you don't need to define them in .cc file, just put the values in .h file:

class Manager
{

  public:

    static const uint8_t NO_BYTES_IN_PACKET = 8;
    static const uint8_t NO_PYLD_BYTES_IN_CONTROL_PACKET = 5;

/* .... */

};

However, if you odr-use those constants, like taking their addresses, then you need to define them in a .cc file, the class definition in .h file remains the same:

const uint8_t Manager::NO_BYTES_IN_PACKET;
const uint8_t Manager::NO_PYLD_BYTES_IN_CONTROL_PACKET;

EDIT: For comments or other answers claiming that static const int can't be used as a compile-time constant, their claim is wrong.

The following is quoted from [class.static.data]/3 (emphasis mine):

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression ([expr.const]). A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both these cases, the member may appear in constant expressions. — end note ] The member shall still be defined in a namespace scope if it is odr-used ([basic.def.odr]) in the program and the namespace scope definition shall not contain an initializer.

llllllllll
  • 16,169
  • 4
  • 31
  • 54
1

The problem is that static const doesn't mean that it's a compile time constant. As an example where it isn't, it could be the time that the program started.

What you want is constexpr that tells the compiler that it can be calculated at compiler time, meaning that your array size is well defined and it conveys your intention much more clearly.

ie

class Manager{
    public:
        constexpr uint8_t NO_BYTES_IN_PACKET = 8;
        ...

In addition; it will force you to resolve the real problem which is that other compilation units are unable to see the size of the constant as you've not defined it in the header.

UKMonkey
  • 6,941
  • 3
  • 21
  • 30
  • Thank you for your reaction. Please can you tell me where I should place the constexpr uint8_t NO_BYTES_IN_PACKET = 8; and constexpr uint8_t NO_PYLD_BYTES_IN_CONTROL_PACKET = 5; – Steve Feb 26 '18 at 16:23
  • @Steve I'm fairly sure you should be able to put them in your header. I've not got a moment though right now to test and be sure. – UKMonkey Feb 26 '18 at 16:31
  • Where exactly in the header do you mean? Anywhere outside the class scope? – Steve Feb 26 '18 at 16:39
  • Your answer is incorrect. See answers [here](https://stackoverflow.com/questions/41125651/constexpr-vs-static-const-which-one-to-prefer). – llllllllll Feb 26 '18 at 16:47
  • The example is improper. Whether the value of a variable can be determined at compile time does not depend on when it is initialized in the abstract machine defined by the standard, because of as-if rule. In fact, as liliscent's answer, if the initializer is in-class, everything is fine. A better explanation would be that the value of this variable is invisible (thus cannot be determined at compile time) in translation units that do not contain the initializer. – xskxzr Feb 26 '18 at 17:04
  • BTW, the standard says: variables with static storage duration are initialized as a consequence of program initiation. – xskxzr Feb 26 '18 at 17:15