I must send some data over a network where the package parts are not byte aligned. All packages are 8 byte long and an example package type might look like this:
union Packed
{
struct
{
uint64_t a : 5;
uint64_t b : 10;
bool c : 1;
};
uint64_t raw;
};
So the first 5 bits are field a the next 10 bits are field b and the last bit is field c. Now I need a send function that can send this and possibly other package types. The function should accept the fields as parameters. The low-level send function accepts a single uint64_t.
Edit As pointed out in the comments, it is not safe to read from raw after writing to a, b or c. To make it clear: this is also something i would like to change, but i included it at the top, because the union is used for all of my attempts.
My requirements are:
An overflow should be detected at compile time
The syntax should not be too bulky (subjective, but i will show what i want)
My first attempt
void send_struct(const Packed& packed)
{
raw_send(packed.raw);
}
int main()
{
send_struct({1000, 2, true}); // conversion changes value
Packed packed = {1000, 2, true}; // conversion changes value
send_struct(packed);
}
The warnings are generated which satisfies my first requirement, but i don't like the syntax: The curly braces look superfluous and manually creating a struct first is cumbersome.
With some warnings enable i even have to use two layers of curly braces, because the struct is nested inside the union.
Second attempt
template <typename ...Args>
void send_var(Args... args)
{
Packed packed {args...};
raw_send(packed.raw);
};
int main()
{
send_var(1000u, 2u, true);
}
Here, i like the syntax, but no warnings are generated, presumably because the bit width is lost somewhere.
Third attempt
struct A
{
uint64_t data : 5;
};
struct B
{
uint64_t data : 10;
};
void send_separate(A a, B b, bool c)
{
Packed packed {a.data, b.data, c};
raw_send(packed.raw);
}
int main()
{
send_separate({1000u}, {2u}, true); // conversion changes value
send_separate(1000u, 2u, true); // compile error
}
The first usage is ugly again: too many curly braces and the second one does not compile, because the structs cannot be implicitly constructed with a single value.
Question
How can i implement a function and a safe package definition such that the following function call compiles and shows a warning, because the value 1000 does not fit into 5 bit.
send(1000u, 2u, true);
I actually only care about the call site. The function and union definitions may be more complicated.
Edit 2
Using variables for the function parameters must should work, too.
uint64_t a, b;
send(a, b, true); // compiles, but may generate a warning
send(a & 0x1f, b & 0x3ff, true); // compiles preferably without a warning
The software will be used on linux only, is compiled with gcc or clang using at lease these warnings: -Wall -Wextra -pedantic plus the flag that allows anonymous structs and unions.
Related
I know this sounds like a silly question, but I would like to know if it is possible in any way to make a custom variable size like this rather than using plain 8, 16, 32 and 64 bit integers:
uint15_t var; //as an example
I did some research online and I found nothing that works on all sizes (only stuff like 12 bit and 24 bit). (I was also wondering if bitfields would work with other data sizes too).
And yes, I know you could use a 16 bit integer to store a 15 bit value, but I just wanted to know if it is possible to do something like this.
How can I make and implement custom integer sizes?
Inside a struct or class, can use the bitfields feature to declare integers of the size you want for a member-variable:
unsigned int var : 15;
... it won't be very CPU-efficient (since the compiler will have to generate bitwise-operations on most accesses to that variable) but it will give you the desired 15-bit behavior.
To be able to use your bitfield int as a normal int but still get the behavior of a 15 bit int you can do it like this :
#include <cassert>
#include <utility>
template<typename type_t, std::size_t N>
struct bits_t final
{
bits_t() = default;
~bits_t() = default;
// explicitly implicit so it can convert automatically from underlying type
bits_t(const type_t& value) :
m_value{ value }
{
};
// implicit conversion back to underlying type
operator type_t ()
{
return m_value;
}
private:
type_t m_value : N;
};
int main()
{
bits_t<int, 15> value;
value = 16383; // 0x3FFF
assert(value == 16383);
// overflow now at bit 15 :)
value = 16384; // 0x4000, 16th bit is set.
assert(value == -16384);
return 0;
}
bitfields feature will do the trick ...
uint32_t customInt : 15;
You can try using bitfields, like many people mentioned. However, bitfields don't have a proper type. If you want to make your arbitrary-sized integers object-oriented, you can stuff the bitfield into a template:
template <int size> struct my_uint
{
uint32_t value: size;
};
typedef my_uint<13> uint13_t; // some people use "using" syntax to do this
typedef my_uint<14> uint14_t;
typedef my_uint<15> uint15_t;
However, now you lost arithmetic operators, and you have to implement (overload) them yourself. You have to ask yourself many questions about what you really want to do with these new types:
Do you want to overload operators like +, *, etc? Which ones?
Do you want to support arrays?
What is the maximum size you want to support? In my example, it's 32.
Do you want to support implicit constructors, e.g. uint15_t(uint32_t)?
How to support overflow?
There is no way to make your new types behave like built-in types - you can come close but cannot quite do it. That is, if you write a big program where you work with uint15_t and later you decide to switch to uint16_t, there will be subtle changes caused by uint16_t being a built-in type (e.g. consider rules about implicit conversions).
I'm toying with a general Bitmask class, based on Type-safe Bitmasks in C++, and here is a minimum example highligthning my issue (Compiler Explorer link here):
#include <type_traits>
#include <cstdint>
#include <iostream>
template<class EnumType,
// Ensure that Bitmask can only be used with enums
typename = std::enable_if_t<std::is_enum_v<EnumType>>>
class Bitmask
{
// Type to store bitmask. Should possibly be bigger
using underlying_type = std::underlying_type_t<EnumType>;
public:
constexpr Bitmask(EnumType option) : m_mask(bitmaskValue(option))
{std::cout << "Bitmask " << (int)m_mask << "\n";}
private:
// m_mask holds the underlying value, e.g. 2 to the power of enum value underlying_type
m_mask{0};
static constexpr underlying_type bitmaskValue(EnumType o)
{ return 1 << static_cast<underlying_type>(o); }
explicit constexpr Bitmask(underlying_type o) : m_mask(o) {}
};
enum class Option : uint8_t
{
a, b, c, d, e, f, g, h, i
};
enum class Option2 : int
{
a, b, c, d, e, f, g, h, i
};
int main()
{
Bitmask<Option> b1{Option::a};
Bitmask<Option> b2{Option::h};
Bitmask<Option> b3{Option::i};
Bitmask<Option2> b4{Option2::i};
}
// Output
Bitmask 1
Bitmask 128
Bitmask 0
Bitmask 256
In short, the Bitmask type works fine as long as the underlying type has more bits than the highest enum value used. If not, the bitmaskValue function will overflow and not give desired result, as shown in output, where b3 gets a value of 0, not 256.
I of course understand that an uint8_t cannot store more than 8 different bits, so what I want is some way to make this a compiler error, either when declaring Bitmask<Option>, or when instatiating a Bitmask with a too high value.
I tried changing the bitmaskValue method to:
static constexpr underlying_type bitmaskValue(EnumType o) {
if constexpr (std::numeric_limits<underlying_type>::digits >= static_cast<underlying_type>(o)) {
return 1 << static_cast<underlying_type>(o);
} else {
// some error
}
}
...but then I get the error that 'o' is not a constant expression.
Can anyone helpt me in the correct direction here? For the record, our project is currently using gcc 9.2/c++17, though I hope we can soon upgrade to gcc 11.1/c++20.
Thanks.
As Davis Herring already pointed out you cannot check for the overflow on a non-constant value at runtime (constexpr marked functions may also be executed at runtime). Thus to get the desired functionality one could check for the overflow inside the declaration of a template with the value to test as non-type template parameter.
The basic idea is that with brace initialization with a constant value, the compiler will generate a compilation error when the cast is narrowing, e.g. int8_t{128}; will fail since 128 cannot be represented with int8_t.
template<class EnumType>
class Bitmask
{
using underlying_type = std::underlying_type_t<EnumType>;
public:
explicit constexpr Bitmask(underlying_type v) : m_mask{v} {}
constexpr Bitmask(EnumType option) : m_mask{0x1 << static_cast<underlying_type>(option)}{}
template <EnumType option> // <- enum value as non-type template parameter
static constexpr auto make()
{
return Bitmask{underlying_type{0x1 << static_cast<underlying_type>(option)}};
}
underlying_type m_mask{0};
};
enum class Option : uint8_t
{
a, b, c, d, e, f, g, h, i
};
Bitmask<Option>::make<Option::f>(); // ok
Bitmask<Option>::make<Option::h>(); // ok
// Bitmask<Option>::make<Option::i>(); // will fail to build
https://godbolt.org/z/117baa4q4
If you control the enum definition as well, the simplest way is just to require an enumerated constant with a fixed name at the end (ie, so it is the largest real value + 1), such as EnumType::MAX_ENUM_VALUE.
Then you can refuse to instantiate if
EnumType::MAX_ENUM_VALUE > std::numeric_limits<underlying_type>::digits + 1
with a static assert, or an enable_if, or whatever. Obviously this will also fail to instantiate if the enumerated constant is missing, so it should be pretty obvious to the user how to fix it.
Note this isn't really a compile-time check for overflow, but for the possibility of overflow. So, it is on the strict side if you only use a subset of your enum's values - but it is consistent with the goal of type safety.
The issue
In a low level bare-metal embedded context, I would like to create a blank space in the memory, within a C++ structure and without any name, to forbid the user to access such memory location.
Right now, I have achieved it by putting an ugly uint32_t :96; bitfield which will conveniently take the place of three words, but it will raise a warning from GCC (Bitfield too large to fit in uint32_t), which is pretty legitimate.
While it works fine, it is not very clean when you want to distribute a library with several hundreds of those warnings...
How do I do that properly?
Why is there an issue in the first place?
The project I'm working on consists of defining the memory structure of different peripherals of a whole microcontroller line (STMicroelectronics STM32). To do so, the result is a class which contains a union of several structures which define all registers, depending on the targeted microcontroller.
One simple example for a pretty simple peripheral is the following: a General Purpose Input/Output (GPIO)
union
{
struct
{
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
};
struct
{
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
uint32_t :32;
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
uint32_t :64;
};
struct
{
uint32_t :192;
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
uint32_t :160;
};
};
Where all GPIO_MAPx_YYY is a macro, defined either as uint32_t :32 or the register type (a dedicated structure).
Here you see the uint32_t :192; which works well, but it triggers a warning.
What I've considered so far:
I might have replaced it by several uint32_t :32; (6 here), but I have some extreme cases where I have uint32_t :1344; (42) (among others). So I would rather not add about one hundred lines on top of 8k others, even though the structure generation is scripted.
The exact warning message is something like:
width of 'sool::ll::GPIO::<anonymous union>::<anonymous struct>::<anonymous>' exceeds its type (I just love how shady it is).
I would rather not solve this by simply removing the warning, but the use of
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-WTheRightFlag"
/* My code */
#pragma GCC diagnostic pop
may be a solution... if I find TheRightFlag. However, as pointed out in this thread, gcc/cp/class.c with this sad code part:
warning_at (DECL_SOURCE_LOCATION (field), 0,
"width of %qD exceeds its type", field);
Which tells us that there is no -Wxxx flag to remove this warning...
How about a C++-ish way?
namespace GPIO {
static volatile uint32_t &MAP0_MODER = *reinterpret_cast<uint32_t*>(0x4000);
static volatile uint32_t &MAP0_OTYPER = *reinterpret_cast<uint32_t*>(0x4004);
}
int main() {
GPIO::MAP0_MODER = 42;
}
You get autocompletion because of the GPIO namespace, and there is no need for dummy padding. Even, it is more clear what's going on, as you can see the address of each register, you don't have to rely on the compiler's padding behavior at all.
Use multiple adjacent anonymous bitfields. So instead of:
uint32_t :160;
for example, you'd have:
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
uint32_t :32;
One for each register you want to be anonymous.
If you have large spaces to fill it may be clearer and less error prone to use macros to repeat the single 32 bit space. For example, given:
#define REPEAT_2(a) a a
#define REPEAT_4(a) REPEAT_2(a) REPEAT_2(a)
#define REPEAT_8(a) REPEAT_4(a) REPEAT_4(a)
#define REPEAT_16(a) REPEAT_8(a) REPEAT_8(a)
#define REPEAT_32(a) REPEAT_16(a) REPEAT_16(a)
Then a 1344 (42 * 32 bit) space can be added thus:
struct
{
...
REPEAT_32(uint32_t :32;)
REPEAT_8(uint32_t :32;)
REPEAT_2(uint32_t :32;)
...
};
In the embedded systems arena, you can model hardware either by using a structure or by defining pointers to the register addresses.
Modeling by structure is not recommended because the compiler is allowed to add padding between members for alignment purposes (although many compilers for embedded systems have a pragma for packing the structure).
Example:
uint16_t * const UART1 = (uint16_t *)(0x40000);
const unsigned int UART_STATUS_OFFSET = 1U;
const unsigned int UART_TRANSMIT_REGISTER = 2U;
uint16_t * const UART1_STATUS_REGISTER = (UART1 + UART_STATUS_OFFSET);
uint16_t * const UART1_TRANSMIT_REGISTER = (UART1 + UART_TRANSMIT_REGISTER);
You could also use the array notation:
uint16_t status = UART1[UART_STATUS_OFFSET];
If you must use the structure, IMHO, the best method to skip addresses would be to define a member and not access it:
struct UART1
{
uint16_t status;
uint16_t reserved1; // Transmit register
uint16_t receive_register;
};
In one of our projects we have both constants and structs from different vendors (vendor 1 uses constants while vendor 2 uses structures).
geza's right that you really don't want to be using classes for this.
But, if you were to insist, the best way to add an unused member of n bytes' width, is simply to do so:
char unused[n];
If you add an implementation-specific pragma to prevent the addition of arbitrary padding to the class's members, this can work.
For GNU C/C++ (gcc, clang, and others that support the same extensions), one of the valid places to put the attribute is:
#include <stddef.h>
#include <stdint.h>
#include <assert.h> // for C11 static_assert, so this is valid C as well as C++
struct __attribute__((packed)) GPIO {
volatile uint32_t a;
char unused[3];
volatile uint32_t b;
};
static_assert(offsetof(struct GPIO, b) == 7, "wrong GPIO struct layout");
(example on the Godbolt compiler explorer showing offsetof(GPIO, b) = 7 bytes.)
To expand on #Clifford's and #Adam Kotwasinski's answers:
#define REP10(a) a a a a a a a a a a
#define REP1034(a) REP10(REP10(REP10(a))) REP10(a a a) a a a a
struct foo {
int before;
REP1034(unsigned int :32;)
int after;
};
int main(void){
struct foo bar;
return 0;
}
To expand on Clifford's answer, you can always macro out the anonymous bitfields.
So instead of
uint32_t :160;
use
#define EMPTY_32_1 \
uint32_t :32
#define EMPTY_32_2 \
uint32_t :32; \ // I guess this also can be replaced with uint64_t :64
uint32_t :32
#define EMPTY_32_3 \
uint32_t :32; \
uint32_t :32; \
uint32_t :32
#define EMPTY_UINT32(N) EMPTY_32_ ## N
And then use it like
struct A {
EMPTY_UINT32(3);
/* which resolves to EMPTY_32_3, which then resolves to real declarations */
}
Unfortunately, you'll need as many EMPTY_32_X variants as many bytes you have :(
Still, it allows you to have single declarations in your struct.
To define a large spacer as groups of 32 bits.
#define M_32(x) M_2(M_16(x))
#define M_16(x) M_2(M_8(x))
#define M_8(x) M_2(M_4(x))
#define M_4(x) M_2(M_2(x))
#define M_2(x) x x
#define SPACER int : 32;
struct {
M_32(SPACER) M_8(SPACER) M_4(SPACER)
};
I think it would be beneficial to introduce some more structure; which may, in turn, solve the issue of spacers.
Name the variants
While flat namespaces are nice, the issue is that you end up with a motley collection of fields and no simple way of passing all related fields together. Furthermore, by using anonymous structs in an anonymous union you cannot pass references to the structs themselves, or use them as template parameters.
As a first step, I would, therefore, consider breaking out the struct:
// GpioMap0.h
#pragma once
// #includes
namespace Gpio {
struct Map0 {
GPIO_MAP0_MODER;
GPIO_MAP0_OTYPER;
GPIO_MAP0_OSPEEDR;
GPIO_MAP0_PUPDR;
GPIO_MAP0_IDR;
GPIO_MAP0_ODR;
GPIO_MAP0_BSRR;
GPIO_MAP0_LCKR;
GPIO_MAP0_AFR;
GPIO_MAP0_BRR;
GPIO_MAP0_ASCR;
};
} // namespace Gpio
// GpioMap1.h
#pragma once
// #includes
namespace Gpio {
struct Map1 {
// fields
};
} // namespace Gpio
// ... others headers ...
And finally, the global header:
// Gpio.h
#pragma once
#include "GpioMap0.h"
#include "GpioMap1.h"
// ... other headers ...
namespace Gpio {
union Gpio {
Map0 map0;
Map1 map1;
// ... others ...
};
} // namespace Gpio
Now, I can write a void special_map0(Gpio:: Map0 volatile& map);, as well as get a quick overview of all available architectures at a glance.
Simple Spacers
With the definition split in multiple headers, the headers are individually much more manageable.
Therefore, my initial approach to exactly meet your requirements would be to stick with repeated std::uint32_t:32;. Yes, it adds a few 100s lines to the existing 8k lines, but since each header is individually smaller, it may not be as bad.
If you are willing to consider more exotic solutions, though...
Introducing $.
It is a little-known fact that $ is a viable character for C++ identifiers; it's even a viable starting character (unlike digits).
A $ appearing in the source code would likely raise eyebrows, and $$$$ is definitely going to attract attention during code reviews. This is something that you can easily take advantage of:
#define GPIO_RESERVED(Index_, N_) std::uint32_t $$$$##Index_[N_];
struct Map3 {
GPIO_RESERVED(0, 6);
GPIO_MAP2_BSRRL;
GPIO_MAP2_BSRRH;
GPIO_RESERVED(1, 5);
};
You can even put together a simple "lint" as a pre-commit hook or in your CI which looks for $$$$ in the committed C++ code and reject such commits.
Although I agree structs should not be used for MCU I/O port access, original question can be answered this way:
struct __attribute__((packed)) test {
char member1;
char member2;
volatile struct __attribute__((packed))
{
private:
volatile char spacer_bytes[7];
} spacer;
char member3;
char member4;
};
You may need to replace __attribute__((packed)) with #pragma pack or similar depending on your compiler syntax.
Mixing private and public members in a struct normally results in that memory layout is no longer guaranteed by C++ standard.
However if all non-static members of a struct are private, it is still considered POD / standard layout, and so are structs that embed them.
For some reason gcc produces a warning if a member of an anonymous struct is private so I had to give it a name. Alternatively, wrapping it into yet another anonymous struct also gets rid of the warning (this may be a bug).
Note that spacer member is not itself private, so data can still be accessed this way:
(char*)(void*)&testobj.spacer;
However such an expression looks like an obvious hack, and hopefully would not be used without a really good reason, let alone as a mistake.
Anti-solution.
DO NOT DO THIS: Mix private and public fields.
Maybe a macro with a counter to generate uniqie variable names will be useful?
#define CONCAT_IMPL( x, y ) x##y
#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
#define RESERVED MACRO_CONCAT(Reserved_var, __COUNTER__)
struct {
GPIO_MAP1_CRL;
GPIO_MAP1_CRH;
GPIO_MAP1_IDR;
GPIO_MAP1_ODR;
GPIO_MAP1_BSRR;
GPIO_MAP1_BRR;
GPIO_MAP1_LCKR;
private:
char RESERVED[4];
public:
GPIO_MAP1_AFRL;
GPIO_MAP1_AFRH;
private:
char RESERVED[8];
};
Since the C++11 transition GCC outputs a warning "enumeral and non-enumeral type in conditional expression". I'd like to understand the reasoning behind this warning. What are the perils of comparing enum constants?
It is obvious we can get rid of this warning by
-Wno-enum-compare
by explicitly casting to an integral type
But why the hassle? Personally, I always strive at writing warning free code, and usually the warnings emitted by default are quite sensible. For example, it figures that it's dangerous to compare signed and unsigned integers.
But using enums is widely used idiomatic C++ metaprogramming. I am not aware of any alternative, which is similarly readable, concise and to the point and does not require any actual storage.
To quote a concrete example: What can go wrong with the following metafunction, so that a warning would be adequate?
template<class TYPES>
struct MaxSize;
template<>
struct MaxSize<NullType>
{
enum{ value = 0 };
};
template<class TY, class TYPES>
struct MaxSize<Node<TY,TYPES> >
{
enum{ thisval = sizeof(TY)
, nextval = MaxSize<TYPES>::value
, value = nextval > thisval? nextval:thisval
};
};
It looks like an issue in GCC. This doesn't depend on -std=c++11 option. The warning is related not to comparison, but to the conditional operator.
Seems that the warning is issued only if one of enum members is iniatialized with an unsigned value, and another one is initialized with the value of another enum. Also, the warning is issued only in the initialization of enum members.
I managed to strip down the code to the following:
enum A { valueA = 1 };
enum B {
// if you change 0u to 0, there'll be no warning
thisval = 0u,
// if you change valueA to any integral constant, there'll be no warning
nextval = valueA,
// warning on this line
value = nextval > thisval ? nextval : thisval,
// no warning here
value2 = nextval > thisval
};
int main() {
// no warning here
(void)(nextval > thisval ? nextval : thisval);
// the same warning also here - but not with Clang
(void)(nextval > thisval ? nextval : false);
}
Online demo
Clang doesn't issue any warnings on this code (except that with -Weverything it complains about unreachable code), though it could really say something on the last line.
(GCC 4.9.2, Clang 3.5.0)
If you are using the enum as a way to define a compile time constant value , you can stop doing that now.
Use this instead:
constexpr int myConstant = 123;
where int can be anything that qualifies as a literal type [I won't try to define literal type here -- it's complicated and Google is your friend.]
Alternatively even before c++11 you could say:
class Foo{
static const int myConstant = 123;
};
where int can be any integral type.
There is no need to have the corresponding definition in a cpp file unless you need the address of myConstant somewhere.
The compiler is telling you that comparing an enum value to anything other than another value in the same enumeration is fragile code.
I can do this on initialization for a struct Foo:
Foo foo = {bunch, of, things, initialized};
but, I can't do this:
Foo foo;
foo = {bunch, of, things, initialized};
So, two questions:
Why can't I do the latter, is the former a special constructor for initialization only?
How can I do something similar to the second example, i.e. declare a bunch of variables for a struct in a single line of code after it's already been initialized? I'm trying to avoid having to do this for large structs with many variables:
Foo foo;
foo.a = 1;
foo.b = 2;
foo.c = 3;
//... ad infinitum
Try this:
Foo foo;
foo = (Foo){bunch, of, things, initialized};
This will work if you have a good compiler (e.g. GCC).
Update: In modern versions of C (but not C++), you can also use a compound literal with designated initializers, which looks like this:
foo = (Foo){ .bunch = 4, .of = 2, .things = 77, .initialized = 8 };
The name right after the "." should be the name of the structure member you wish to initialize. These initializers can appear in any order, and any member that is not specified explicitly will get initialized to zero.
The first is an aggregate initializer - you can read up on those and tagged initializers at this solution:
What is tagged structure initialization syntax?
It is a special initialization syntax, and you can't do something similar after initialization of your struct. What you can do is provide a member (or non-member) function to take your series of values as parameters which you then assign within the member function - that would allow you to accomplish this after the structure is initialized in a way that is equally concise (after you've written the function the first time of course!)
In C++11 you can perform multiple assignment with "tie" (declared in the tuple header)
struct foo {
int a, b, c;
} f;
std::tie(f.a, f.b, f.c) = std::make_tuple(1, 2, 3);
If your right hand expression is of fixed size and you only need to get some of the elements, you can use the ignore placeholder with tie
std::tie(std::ignore, f.b, std::ignore) = some_tuple; // only f.b modified
If you find the syntax std::tie(f.a, f.b, f.c) too code cluttering you could have a member function returning that tuple of references
struct foo {
int a, b, c;
auto members() -> decltype(std::tie(a, b, c)) {
return std::tie(a, b, c);
}
} f;
f.members() = std::make_tuple(1, 2, 3);
All this ofcourse assuming that overloading the assignment operator is not an option because your struct is not constructible by such sequence of values, in which case you could say
f = foo(1, 2, 3);
Memory Footprint - Here is an interesting i386 addition.
After much hassle, using optimization and memcpy seems to generate the smallest footprint using i386 with GCC and C99. I am using -O3 here. stdlib seems to have all sorts of fun compiler optimizations at hand, and this example makes use of that (memcpy is actually compiled out here).
Do this by:
Foo foo; //some global variable
void setStructVal (void) {
const Foo FOO_ASSIGN_VAL = { //this goes into .rodata
.bunch = 1,
.of = 2,
.things = 3,
.initialized = 4
};
memcpy((void*) &FOO_ASSIGN_VAL, (void*) foo, sizeof(Foo));
return;
}
Result:
(.rodata) FOO_ASSIGN_VAL is stored in .rodata
(.text) a sequence of *movl FOO_ASSIGN_VAL, %registers* occur
(.text) a sequence of movl %registers, foo occur
Example:
Say Foo was a 48 field struct of uint8_t values. It is aligned in memory.
(IDEAL) On a 32-bit machine, this COULD be as quick as 12 MOVL instructions of immediates out to foo's address space. For me this is 12*10 == 120bytes of .text in size.
(ACTUAL) However, using the answer by AUTO will likely generate 48 MOVB instructions in .text. For me this is 48*7 == 336bytes of .text!!
(SMALLEST*) Use the memcpy version above. IF alignment is taken care of,
FOO_ASSIGN_VAL is placed in .rodata (48 bytes),
12 MOVL into %register
12 MOVL outof %registers are used in .text (24*10) == 240bytes.
For me then this is a total of 288 bytes.
So, for me at least with my i386 code,
- Ideal: 120 bytes
- Direct: 336 bytes
- Smallest: 288 bytes
*Smallest here means 'smallest footprint I know of'. It also executes faster than the above methods (24 instructions vs 48). Of course, the IDEAL version is fastest & smallest, but I still can't figure that out.
-Justin
*Does anyone know how to get implementation of 'IDEAL' above? It is annoying the hell out of me!!
If you don't care too much about efficiency, you could double assign: i.e. create a new instance of the structure using aggregate initialization, and then copy it over:
struct Foo foo;
{
struct Foo __tmp__ = {bunch, of, things, initialized};
foo = __tmp__;
}
Make sure you keep the portion wrapped in {}s so as to discard the unnecessary temporary variable as soon as it's no longer necessary.
Note this isn't as efficient as making, e.g., a 'set' function in the struct (if c++) or out of the struct, accepting a struct pointer (if C). But if you need a quick, preferably temporary, alternative to writing element-by-element assignment, this might do.
If you care about efficiency, you can define a union of the same length as your structure, with a type you can assign at once.
To assign values by elements use the struct of your union, to assign the whole data, use the other type of your union.
typedef union
{
struct
{
char a;
char b;
} Foo;
unsigned int whole;
} MyUnion;
MyUnion _Union;
_Union.Foo.a = 0x23; // assign by element
_Union.Foo.b = 0x45; // assign by element
_Union.whole = 0x6789; // assign at once
Be carefull about your memory organization (is "a" the MSB or the LSB of "whole"?).