3

Sorry if this is solvable via Google -- I couldn't find anything.

char foo[4] = "abcd";

is invalid in C++ (due to the need for '\0' terminator) but IIRC valid in C -- is that correct?

I have a set of structs with a large number of "fixed length" character fields that are to be blank-padded and not '\0' terminated. I would like to be able to do the usual sort of struct initialization -- you know

mystruct bar = {17, "abcd", 0x18, "widget  ", ...

But I can't do that in C++. One solution I guess would be to put all of the initialized structs like this into their own C (not ++) source module. The ugly, laborious solution that I am trying to avoid is

mystruct bar = {17, {'a', 'b', 'c', 'd'}, 0x18, {'w', 'i', 'd', 'g', 'e', 't', ' ', ' '}, ...

Is there a good C++ solution? A clever macro that will effectively let "abcd" be char[4] with no '\0' terminator?

Thanks, Charles

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Charles
  • 479
  • 1
  • 3
  • 13

3 Answers3

1

Give the struct a constructor that accepts char arrays of size 1 greater than what you need, and ignore the terminating character when copying them over.

bar::bar(int, char const(&)[5], int, char const(&)[9], ...)

Or, you can just make the parameters char const* and trust the user to pass correctly sized arrays, in accordance with your documentation. If you don't want to, or can't, add anything to the struct, then just create a function with those same arguments which returns one (RVO should eliminate extra copying).

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
0

I think it will work as a uint8 array (or whatever your compiler supports). Just don't ever think it's a string, because it is not. It's MUCH safer the allocate an extra char and initialise to zero (usually automatic). It is better still to use STL components, unless there is a very good reason to do something else.

C++ is not C. In C++ the compiler tries to keep you safe. In C it is entirely your own lookout.

davidc
  • 132
  • 1
  • 4
  • I love the STL and I love C++. No problem there. In this particular situation I have a large number of externally-fixed "record formats" imposed upon me, and they are full of this sort of char array. I need to build a handful of them in code for test purposes. I am looking for a neat way to do so. – Charles Oct 28 '14 at 16:47
  • My sandbox but not my final platform is MS VS C++. At least there, UINT8 is just a macro for unsigned char and makes no difference. The compiler seems to have a native type __int8 but it is no improvement. – Charles Oct 28 '14 at 16:58
  • You could consider adding the terminating null on import or whenever, as the extra char in the array would do. Your internal representation is not bound by the external representation. – davidc Oct 28 '14 at 17:01
0

You could use a macro that will do the job:

#define MACRO_GET_1(str, i) \
    (sizeof(str) > (i) ? str[(i)] : 0)

#define MACRO_GET_4(str, i) \
    MACRO_GET_1(str, i+0),  \
    MACRO_GET_1(str, i+1),  \
    MACRO_GET_1(str, i+2),  \
    MACRO_GET_1(str, i+3)

#define MACRO_GET_STR(str) MACRO_GET_4(str, 0)

struct mystruct {
    char foo[4];
};

int main() {
    mystruct obj{ MACRO_GET_STR("abcd") };
    const char* str = "abcd";
    std::cout << memcmp(obj.foo, str, 4); // 0

    return 0;
}

Example

This code is based on this solution and it is also easily extendable.

Anyway there was a proposal some time ago that probably never made it to the standard. There are patches around that allow string literals to be converted into variadic char packs.

Community
  • 1
  • 1
Marco A.
  • 43,032
  • 26
  • 132
  • 246
  • Thanks! That should do it. Most of the problematic arrays are 8 characters in length or a few other "standard" sizes, so I will need 3 or 4 such macros -- and the rest I can do the ugly way. – Charles Oct 28 '14 at 17:07
  • Are you sure that sizeof() will work as you intended in the first macro? I wanted to blank-pad rather than zero-pad the results, so I changed it to sizeof(str) >= (i) ? str[(i)] : ' ' and several variants thereof. No matter what I do the resulting array is zero-padded. My mental model is that the expression is always true and str[(i)] evaluates to '\0' for all i < the length of str. Your take? – Charles Oct 28 '14 at 22:43
  • I suppose you will have to play with the sizes to avoid zero-padding if that's undesirable – Marco A. Oct 28 '14 at 23:00
  • Confirming: sizeof() does not work in a macro for most compilers: http://stackoverflow.com/questions/4079243/how-can-i-use-sizeof-in-a-preprocessor-macro – Charles Oct 28 '14 at 23:01
  • Are you string literals allowed to be constexpr? If so, C++11 might help with this – Marco A. Oct 28 '14 at 23:03
  • No constexpr. Using VS 2010. – Charles Oct 28 '14 at 23:44
  • Still playing around but I *seem* to be able to take advantage of the fact that str[(some large value)] == 0 by using str[(i)] != 0 ? str[(i)] : ' '. I'm sure this behavior is very compiler-dependent. – Charles Oct 28 '14 at 23:45
  • The solution may be simplified somewhat in that there is no need to pass a subscript to the internal macros. You can pass (n+str) as a single argument -- parentheses required. (3+"abcdefg")[2] is valid at least on my compiler and yields the expected 'f'. – Charles Oct 29 '14 at 20:18