I need to use a very large and complex class just for the title (I think boost :: multiprecision :: cpp_bin_float <76>, called BHP
below), which I would like to hide behind an implementation like pimpl, to reduce compilation time in a somewhat large project (replacing the Boost class std::complex<double>
reduced compilation time by about 50%).
However, I would like to avoid dynamic memory allocations. Therefore, something like this seems natural (ignoring the alignment problems at the moment, which can be avoided with aligned_storage
or alignas
):
struct Hidden {
char data[sz];
Hidden& punned(Hidden const& other);
};
Hidden::punned
can then be defined in one translation block to be dropped data
in BHP*
, act on it and not pollute all other translation units with 170k LOC header files. Possible implementation may be
Hidden& Hidden::punned(Hidden const& other) {
*(BHP*)(data) += *(BHP*)(other.data);
return *this;
}
This, of course, is undefined behavior, because we access the type object BHP
using a type pointer char
, thereby violating the strict rules of aliases. The correct way to do this is:
Hidden& Hidden::proper(Hidden const& other) {
BHP tmp; std::memcpy(&tmp, data, sz);
BHP tmp2; std::memcpy(&tmp2, other.data, sz);
tmp += tmp2;
std::memcpy(data, &tmp, sz);
return *this;
}
Now it may seem “obvious that these challenges memcpy
can be optimized. Unfortunately, this is not so, they remain and do proper()
much more than that punned()
.
, : a) Hidden
b) c) d) , .
godbolt; , (GCC 4.9 - trunk, Clang 3.9, 4.0 5.0 Intel 18) memcpy. GCC (, 5.3) , . Ive Direct
, BHP
, , , .
:
#include <cstring>
constexpr std::size_t sz = 64;
struct Base {
char foo[sz];
Base& operator+=(Base const& other) { foo[0] += other.foo[0]; return *this; }
};
typedef Base BHP;
struct Hidden {
char data[sz];
Hidden& proper(Hidden const& other);
Hidden& punned(Hidden const& other);
};
Hidden& Hidden::proper(Hidden const& other) {
BHP tmp; std::memcpy(&tmp, data, sz);
BHP tmp2; std::memcpy(&tmp2, other.data, sz);
tmp += tmp2;
std::memcpy(data, &tmp, sz);
return *this;
}
Hidden& Hidden::punned(Hidden const& other) {
*(BHP*)(data) += *(BHP*)(other.data);
return *this;
}
struct Direct {
BHP member;
Direct& direct(Direct const& other);
};
Direct& Direct::direct(Direct const& other) {
member += other.member;
return *this;
}
struct Pointer {
char storage[sz];
BHP* data;
Pointer& also_ok(Pointer const& other);
};
Pointer& Pointer::also_ok(Pointer const& other) {
*data += *other.data;
return *this;
}