Much of it is possible with C++26. So no need for a language change or new syntax?

 

 

Sorry, I asked ChatGPT to create the code as a simple template, first the usage, then the underlying code:

 

#include <cstdint>
#include <type_traits>

 

 

// our aligned struct


struct Message {
  std::uint16_t kind;
  std::uint32_t length;
  double value;
};

 

// create an unaligned version

 

using UnalignedMessage = unaligned_struct_t<Message>;

 

 

// usage

 

int main() {
  UnalignedMessage m{};

  m.kind = std::uint16_t{7};
  m.length = std::uint32_t{1024};
  m.value = 3.5;

  std::uint16_t kind = m.kind.load();
  std::uint32_t len = m.length;
  double value = m.value;
}

 

// test sizes and types to prove that it works

 

static_assert(std::same_as<
   decltype(UnalignedMessage::kind),
  unaligned<std::uint16_t>
>);

static_assert(std::same_as<
   decltype(UnalignedMessage::length),
  unaligned<std::uint32_t>
>);

static_assert(std::same_as<
  decltype(UnalignedMessage::value),
  unaligned<double>
>);

static_assert(alignof(UnalignedMessage) == 1);
static_assert(sizeof(UnalignedMessage) == sizeof(std::uint16_t) + sizeof(std::uint32_t) + sizeof(double));

 

 

// implementation

 

#include <cstddef>
#include <cstring>
#include <meta>
#include <type_traits>
#include <vector>
 

 

// make a single type unaligned -> std::unaligned<T>?


template<class T>
class unaligned {
   using object_type = std::remove_cv_t<T>;

  static_assert(std::is_object_v<T>);
  static_assert(!std::is_reference_v<T>);
  static_assert(!std::is_array_v<T>);
  static_assert(!std::is_volatile_v<T>);
  static_assert(std::is_trivially_copyable_v<object_type>);

  std::byte bytes_[sizeof(object_type)]{};

public:
  using value_type = T;

  unaligned() = default;

  explicit unaligned(T const& value) noexcept {
    store(value);
  }

  auto load() const noexcept -> T {
    object_type value;
    std::memcpy(&value, bytes_, sizeof(value));
    return value;
  }

  auto store(T const& value) noexcept -> void {
    std::memcpy(bytes_, &value, sizeof(value));
  }

  auto operator=(T const& value) noexcept -> unaligned& {
     store(value);
    return *this;
  }

  operator T() const noexcept {
    return load();
  }
};

static_assert(alignof(unaligned<int>) == 1);
static_assert(sizeof(unaligned<int>) == sizeof(int));

 

namespace detail {

consteval void validate_unaligned_source(std::meta::info source) {
    using namespace std::meta;

    constexpr auto ctx = access_context::unchecked();

    if (!bases_of(source, ctx).empty()) {
        throw "source type must not have base classes";
    }

    for (info member : members_of(source, ctx)) {
        // Ignore implicitly declared special member functions.
        if (is_function(member) && !is_user_declared(member)) {
            continue;
        }

        if (!is_nonstatic_data_member(member)) {
            throw "source type must declare only non-static data members";
        }

        if (!is_public(member)) {
            throw "all source data members must be public";
        }

        if (!has_identifier(member)) {
            throw "all source data members must have names";
        }

        if (is_bit_field(member)) {
            throw "bit-fields are not supported";
        }
    }
}

 

consteval auto make_unaligned_member_specs(std::meta::info source)
    -> std::vector<std::meta::info>
{
    using namespace std::meta;

    validate_unaligned_source(source);

    constexpr auto ctx = access_context::unchecked();

    std::vector<info> specs;

    for (info member : nonstatic_data_members_of(source, ctx)) {
        info member_type = type_of(member);
        info wrapped_type = substitute(^^unaligned, {member_type});

        specs.push_back(
            data_member_spec(
                wrapped_type,
                {.name = identifier_of(member)}
            )
        );
    }

    return specs;
}

} // namespace detail

template<class Source>
struct unaligned_struct {
    static_assert(std::is_class_v<Source>);
    static_assert(std::is_aggregate_v<Source>);

    struct type;

    consteval {
        std::meta::define_aggregate(
            ^^type,
            detail::make_unaligned_member_specs(^^Source)
        );
    }
};

template<class Source>
using unaligned_struct_t = typename unaligned_struct<Source>::type;

 


 

-----Ursprüngliche Nachricht-----
Von: Alejandro Colomar via Std-Proposals <std-proposals@lists.isocpp.org>
Gesendet: Mo 08.06.2026 14:50
Betreff: Re: [std-proposals] Achieving pragma pack(1) with typedef<unaligned>
Anlage: signature.asc
An: Frederick Virchanza Gotham via Std-Proposals <std-proposals@lists.isocpp.org>;
CC: Alejandro Colomar <une+cxx_std-proposals@alejandro-colomar.es>;
Hi Frederick,

On 2026-06-08T13:21:27+0100, Frederick Virchanza Gotham via Std-Proposals wrote:
> On Mon, Jun 8, 2026 at 1:11 AM Alejandro Colomar wrote:
> >
> > Also, keeping a struct K that is not packed is going to be error prone.
> > One might use struct J thinking it's also packed.
>
>
> struct K_unpacked { . . .  };
>
> typedef< std::unaligned > K_unpacked K;

I still don't see why you'd want to keep a struct that you don't want.

It might make more sense as

typedef<std::unaligned> struct {...} K;

But needing typedef to make a structure unaligned seems to be
a workaround.  I'd rather make it a type specifier _Packed, or an
unignorable attribute such as [[::packed]].  We also need this in C,
FWIW.

> > Have a lovely night!
>
>
> It's a little after noon where I am.

Have a lovely night when it arrives!  :)
Alex

--
<https://www.alejandro-colomar.es>
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals