Initializing complex, const, variably-sized structures at compiletime

S

spacewrench

I'm working on a USB device framework for a microcontroller. USB
requires manipulating many descriptor structures that are variably-
sized. For example:

struct StringDescriptor {
unsigned len;
wchar_t data[0];
}

(That's simpler than a real USB StringDescriptor, but it shows the
essential feature, a variably-sized array of wchar_t's that contain
the string data.)

I would like to be able to declare these at compile time, and place
them in Flash if appropriate:

StringDescriptor might_be_modified( "Some String Value" );
const StringDescriptor cannot_be_modified( "Fixed String Value" );

Is there a clean way to declare StringDescriptor so that the
declarations are simple and don't involve malloc(), new() or a run-
time constructor invocation? All the code examples I've seen just
declare an array of bytes, which has no type safety and poor
readability.

Thanks,
 
I

Ian Collins

I'm working on a USB device framework for a microcontroller. USB
requires manipulating many descriptor structures that are variably-
sized. For example:

struct StringDescriptor {
unsigned len;
wchar_t data[0];
}

(That's simpler than a real USB StringDescriptor, but it shows the
essential feature, a variably-sized array of wchar_t's that contain
the string data.)

I would like to be able to declare these at compile time, and place
them in Flash if appropriate:

StringDescriptor might_be_modified( "Some String Value" );
const StringDescriptor cannot_be_modified( "Fixed String Value" );
Something like (simplified with char):

template <size_t N>
struct StringDescriptor {
unsigned len;
char data[N];
};

const char* fixed = "Fixed String Value";

const StringDescriptor<sizeof("Fixed String Value")> cannot_be_modified
= {sizeof("Fixed String Value"), "Fixed String Value" };
 
S

spacewrench

Thanks for the replies. I thought there might be an arcane
template<T> way of doing this, but perhaps not. I don't mind a GCC-
only solution, so I'm currently investigating a cpp macro that turns
into asm("..."). I've got the first part of it working OK (gas-2.19
has a ".string16" pseudo-op that comes in handy!). Next is figuring
out how to turn these asm blobs into small-integer indices into an
array of pointers to StringDescriptors.

The ultimate goal is to be able to say:

DeviceDescriptor x = {
.ManufacturerString = CONST_STRING_DESCRIPTOR( "Acme Industries" ),
.ProductString = STRING_DESCRIPTOR( "Frobozz 3200" )
} ;

[Struct initialization syntax might be wrong, but you get the idea.]

....with ManufacturerString set to n and ProductString set to (n+1),
and GlobalStringDescriptors[n] = StringDescriptor( "Acme
Industries" ), GlobalStringDescriptors[n+1] = StringDescriptor
( "Frobozz 3200" ). (The "Acme Industries" string should live in
Flash / .rodata.)

I'll post the solution if I find one.

Regards,
d.
 
M

Michael DOUBEZ

Ian Collins a écrit :
(e-mail address removed) wrote:
[snip]
Something like (simplified with char):

template <size_t N>
struct StringDescriptor {
unsigned len;
char data[N];
};

const char* fixed = "Fixed String Value";

You must mean:
const char fixed[] = "Fixed String Value";
 
J

James Kanze

I'm working on a USB device framework for a microcontroller.
USB requires manipulating many descriptor structures that are
variably- sized. For example:
struct StringDescriptor {
unsigned len;
wchar_t data[0];
}

That's not a legal definition, neither in C nor in C++.
(That's simpler than a real USB StringDescriptor, but it shows
the essential feature, a variably-sized array of wchar_t's
that contain the string data.)

In fact, what you want is a flexible array member: an incomplete
array type as the last element (in which case, the final
declaration should be "wchar_t data[];"). That's only supported
in C99, not C++ nor C90.

Some compilers may allow it as an extension. (I believe that
some compilers also allow the 0 sized array in this case, again
as an extension.)
I would like to be able to declare these at compile time, and
place them in Flash if appropriate:
StringDescriptor might_be_modified( "Some String Value" );
const StringDescriptor cannot_be_modified( "Fixed String Value" );
Is there a clean way to declare StringDescriptor so that the
declarations are simple and don't involve malloc(), new() or a
run- time constructor invocation?

struct StringDescriptor
{
unsigned length ;
wchar_t* data ;
} ;

wchar_t const string_cannot_be_modifed[]
= L"Fixed String Value" ;
StringDescriptor const
cannot_be_modified =
{
sizeof( string_cannot_be_modifed ) - 1,
string_cannot_be_modifed
} ;
All the code examples I've seen just declare an array of
bytes, which has no type safety and poor readability.

One can argue about readability; for something like the above,
I'll usually use a simple script to generate it, rather than
write it all out by hand.
 
J

James Kanze

Ian Collins a écrit :
(e-mail address removed) wrote:
[snip]
Something like (simplified with char):
template <size_t N>
struct StringDescriptor {
unsigned len;
char data[N];
};
const char* fixed = "Fixed String Value";
You must mean:
const char fixed[] = "Fixed String Value";

It doesn't matter, since he never uses it.

The problem with this solution is that you don't have a simple
StringDescriptor type, which is probably what his functions
expect.
 
M

Michael DOUBEZ

James Kanze a écrit :
Ian Collins a écrit :
(e-mail address removed) wrote:
[snip]
Something like (simplified with char):
template <size_t N>
struct StringDescriptor {
unsigned len;
char data[N];
};
const char* fixed = "Fixed String Value";
You must mean:
const char fixed[] = "Fixed String Value";

It doesn't matter, since he never uses it.

Funny how mind works.
The problem with this solution is that you don't have a simple
StringDescriptor type, which is probably what his functions
expect.

Not necessarily, he can get away with default parameters and mutation.

template <size_t N = 0 /* or any minimal size */>
struct StringDescriptorImp {
unsigned len;
char data[N];

//define only const version since N>0 should reside in RO data
operator const StringDescriptorImp<>&()const{
assert( N >= 0 /* minimal size */ );
return *reinterpret_cast<constStringDescriptorImp<>*>(this);
}
};

typedef StringDescriptorImp<> StringDescriptor;

So he gets a default StringDescriptor type and can use
StringDescriptorImp to place data somewhere in flash.

I admit the cast is a bit ugly and formally invoke undefined behavior
but in embedded systems, this is usually under control.
 
M

Michael DOUBEZ

James Kanze a écrit :
I'm working on a USB device framework for a microcontroller.
USB requires manipulating many descriptor structures that are
variably- sized. For example:
struct StringDescriptor {
unsigned len;
wchar_t data[0];
}

That's not a legal definition, neither in C nor in C++.
(That's simpler than a real USB StringDescriptor, but it shows
the essential feature, a variably-sized array of wchar_t's
that contain the string data.)

In fact, what you want is a flexible array member: an incomplete
array type as the last element (in which case, the final
declaration should be "wchar_t data[];"). That's only supported
in C99, not C++ nor C90.

Some compilers may allow it as an extension. (I believe that
some compilers also allow the 0 sized array in this case, again
as an extension.)

I know IAR and ARM tools do support it. I believe it is quite common.
Perhaps it is part of the embedded-C++ specs, I have never checked.
I would like to be able to declare these at compile time, and
place them in Flash if appropriate:
StringDescriptor might_be_modified( "Some String Value" );
const StringDescriptor cannot_be_modified( "Fixed String Value" );
Is there a clean way to declare StringDescriptor so that the
declarations are simple and don't involve malloc(), new() or a
run- time constructor invocation?

struct StringDescriptor
{
unsigned length ;
wchar_t* data ;
} ;

wchar_t const string_cannot_be_modifed[]
= L"Fixed String Value" ;
StringDescriptor const
cannot_be_modified =
{
sizeof( string_cannot_be_modifed ) - 1,
string_cannot_be_modifed
} ;

As I understand the OP problem, he wants to put them in RO area and use
them directly to answer some request. What he is actually describing is
the answer message (or part of it) and the layout wanted is the
serialized version of the data.
One can argue about readability; for something like the above,
I'll usually use a simple script to generate it, rather than
write it all out by hand.

IMHO, this is the few case when a macro is justified. This is what we
use when describing the layout of the configuration space in flash on
our system.
 
J

James Kanze

James Kanze a écrit :
I'm working on a USB device framework for a microcontroller.
USB requires manipulating many descriptor structures that are
variably- sized. For example:
struct StringDescriptor {
unsigned len;
wchar_t data[0];
}
That's not a legal definition, neither in C nor in C++.
(That's simpler than a real USB StringDescriptor, but it shows
the essential feature, a variably-sized array of wchar_t's
that contain the string data.)
In fact, what you want is a flexible array member: an
incomplete array type as the last element (in which case,
the final declaration should be "wchar_t data[];"). That's
only supported in C99, not C++ nor C90.
Some compilers may allow it as an extension. (I believe
that some compilers also allow the 0 sized array in this
case, again as an extension.)
I know IAR and ARM tools do support it. I believe it is quite
common. Perhaps it is part of the embedded-C++ specs, I have
never checked.

The only compiler I have that accepts it with my usual options
is VC++. G++ will accept it as well, IF you forget to specify
-std=c++98. Never the less, the C standard explicitly forbids
it (and C++ just copies C here).
I would like to be able to declare these at compile time,
and place them in Flash if appropriate:
StringDescriptor might_be_modified( "Some String Value" );
const StringDescriptor cannot_be_modified( "Fixed String Value" );
Is there a clean way to declare StringDescriptor so that
the declarations are simple and don't involve malloc(),
new() or a run- time constructor invocation?
struct StringDescriptor
{
unsigned length ;
wchar_t* data ;
} ;
wchar_t const string_cannot_be_modifed[]
= L"Fixed String Value" ;
StringDescriptor const
cannot_be_modified =
{
sizeof( string_cannot_be_modifed ) - 1,
string_cannot_be_modifed
} ;
As I understand the OP problem, he wants to put them in RO
area and use them directly to answer some request. What he is
actually describing is the answer message (or part of it) and
the layout wanted is the serialized version of the data.

You mean that he wants to be able to do a simple write of the
bytes, with no formatting? That wouldn't be very clean to begin
with. (On the other hand, in some embedded environments, it
might be justified.) The only real way to do that is with a
char const[]; it wouldn't be too difficult to generate them
automatically.
IMHO, this is the few case when a macro is justified. This is
what we use when describing the layout of the configuration
space in flash on our system.

Nothing against a macro in this case either, but I think that
automatic generation will work better, since it can count the
characters for you (although come to think of it, you could
probably do this with a macro as well, as long as the macro was
only used with string literals).
 
S

spacewrench

What he is actually describing is
the answer message (or part of it) and the layout wanted is the
serialized version of the data.

Yes, that's it. I should have thought to describe it that way. I
need the descriptors in serialized form for some purposes, and the
information is accessible easily enough in that form, so I can save
code & data space by simply storing & using them in serialized form
always. My question should have been, how to get the compiler to put
them in that form, without the editing/maintenance hassle of what
everybody else seems to do:

const char FooStrDscr[] = { 6, 'F', 0, 'o', 0, 'o', 0 } ;
const char WazooStrDscr[] = { 10, 'W', 0, 'a', 0, 'z', 0, 'o', 0, 'o',
0 };

const char * const StrDscrTbl[] = { FooStrDscr, WazooStrDscr };

#define FooStrDscrIdx 0
#define WazooStrDscrIdx 1

I could probably hack something together using lots of GNU features,
but when I started looking at the linker scripts (necessary, I think,
to put all the StrDscr pointers in a single table so I can
automagically generate integer indices to reference them), it's
hairier than the standard approach. Given how little time the program
spends fiddling with these descriptors anyway, and the fact that they
don't get changed _that_ often, it makes sense to waste my time
elsewhere.

Thanks, everyone.
 
V

Vidar Hasfjord

I'm working on a USB device framework for a microcontroller. USB
requires manipulating many descriptor structures that are variably-
sized. For example:

struct StringDescriptor {
unsigned len;
wchar_t data[0];
}

[...]

I would like to be able to declare these at compile time, and place
them in Flash if appropriate:

StringDescriptor might_be_modified( "Some String Value" );
const StringDescriptor cannot_be_modified( "Fixed String Value" );

Is there a clean way to declare StringDescriptor so that the
declarations are simple and don't involve malloc(), new() or a run-
time constructor invocation? All the code examples I've seen just
declare an array of bytes, which has no type safety and poor
readability.

Yes. To give the descriptor proper type in C++ you have to pry apart
the static and dynamic parts, i.e. the static descriptor head and the
dynamic data part. Then use an accessor function to access the data
part:

struct StringDescriptor {
unsigned len;
};

inline
const wchar_t* data (const StringDescriptor& s) {
assert (s.len > 0);
return reinterpret_cast <const wchar_t*>
(reinterpret_cast <const char*> (&s) + sizeof (s));
}

Here I've used a free function, but if you prefer you can of course
make it a member. Usage:

void process_str (const StringDescriptor& s) {
wcout << data (s) << endl;
}

To address the second part of your post, initialization, you can use a
template with a conversion function:

template <size_t N>
struct StringDescriptorN {
unsigned len;
wchar_t data [N];
operator const StringDescriptor& () const {
return reinterpret_cast <const StringDescriptor&>
(*this);
}
};

Usage:

const StringDescriptorN <6> foo = {6, L"Foo"};
process_str (foo);

To further simplify declarations, you can use a macro:

#define DECL_STR_DSCR(name, str) \
StringDescriptorN <sizeof (str)> name = \
{sizeof (str), str};

Usage:

const DECL_STR_DSCR (wazoo, L"Wazoo");
process_str (wazoo);

You can make similar templates and macros to handle your descriptor
tables.

Regards,
Vidar Hasfjord
 
S

spacewrench

To address the second part of your post, initialization, you can use a
template with a conversion function:

  template <size_t N>
  struct StringDescriptorN {
    unsigned len;
    wchar_t data [N];
    operator const StringDescriptor& () const {
      return reinterpret_cast <const StringDescriptor&>
        (*this);
    }
  };

Ooh, that's a great idea! Except I could even do:

struct StringDescriptor {
unsigned len;
const wchar_t *get_data( void ) const { return (wchar_t *)(&len +
1); }
} ;

template <size_t N>
struct StringDescriptorBody : public StringDescriptor {
wchar_t data[N];
} ;

Then I don't even need the conversion operator in the template. (Or
perhaps I still do, for some reason that my c++-fu is not yet adequate
to understand. I'll have to play with this a bit. There's still the
problem of getting the wchar_t's into little-endian order on machines
where that's not the default...yuck.)

Thanks!
 
V

Vidar Hasfjord

On Dec 24, 4:50 am, Vidar Hasfjord <[email protected]>
wrote:
[...]

Ooh, that's a great idea!  Except I could even do:

struct StringDescriptor {
  unsigned len;
  const wchar_t *get_data( void ) const { return (wchar_t *)(&len +
1); }

} ;

Yes, my cast to char pointer was unnecessary. I prefer
reinterpret_cast though.
template <size_t N>
struct StringDescriptorBody : public StringDescriptor {
  wchar_t data[N];

} ;

Then I don't even need the conversion operator in the template.  (Or
perhaps I still do, for some reason that my c++-fu is not yet adequate
to understand.

Unfortunately, that will not work. You cannot use inheritance, because
initializer lists can only be used with POD types. Hence the
conversion operator is needed.
 I'll have to play with this a bit.  There's still the
problem of getting the wchar_t's into little-endian order on machines
where that's not the default...yuck.)

That sounds like a IO problem that hopefully is orthogonal to the
initialization issue.

See below for corrected and simplified code that should work with
Windows Driver Kit's USB library. Here the template converts to the
Windows' struct which has a placeholder string member of size 1.

namespace usb {


const UCHAR string_descriptor_typetag =
USB_STRING_DESCRIPTOR_TYPE;


template <size_t N>
struct StringDescriptor {
UCHAR bLength; // size in bytes of descriptor
UCHAR bDescriptorType;
wchar_t bString [N];

operator const USB_STRING_DESCRIPTOR& () const {
assert (bLength == sizeof (*this));
assert (bDescriptorType == string_descriptor_typetag);
return reinterpret_cast <const USB_STRING_DESCRIPTOR&>
(*this);
}
};


template <>
struct StringDescriptor <0>; // undefined


} // namespace


// Test utilities


void log (const USB_STRING_DESCRIPTOR& s) {
wcout << "USB_STRING_DESCRIPTOR {"
<< "length = " << s.bLength
<< ", type = " << s.bDescriptorType
<< ", string = " << s.bString
<< "}" << endl;
}


#define ARRAY_SIZE(a) \
(sizeof (a) / sizeof (a [0]))

#define DECL_USB_STR_DSCR(name, str) \
usb::StringDescriptor <ARRAY_SIZE (str)> name = \
{sizeof (name), usb::string_descriptor_typetag, str};


void test_variable_array ()
{
const DECL_USB_STR_DSCR (foo, L"Foo");
log (foo);

const DECL_USB_STR_DSCR (wazoo, L"Wazoo");
log (wazoo);
}

Regards,
Vidar Hasfjord
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,439
Members
44,829
Latest member
PIXThurman

Latest Threads

Top