Ayyıldız2 | 2008 TR Yapısı • 1-99 Orta Emek Destan • Oto Avsız • 10 Temmuz 21:00 HEMEN TIKLA!
Trivially-copyable paket struct'larını ve değişken payload'ları bir buffer'a serialize eidp, istediğiniz bir send stratejisiyle gönderen(tabii default-initializable functor/stateless lambda olmak koşuluyla) fluent, move-only bir builder.
"Neden böyle bir şeye ihtiyacımız olsun?" derseniz, paket gönderirken şu hataları compile-time'da yakalarsınız:
-> padding'li bir struct'ı raw byte olarak alakalı buffer'a yazmak
-> pointer serialize etmeye çalışmak
Contiguous, forward range veya bir POD'u argüman olarak geçebilirsiniz.
Fayda olarak da compile-time validation, type safety sayılabilir. Tabii code-base'in %99u buna uymuyor ki biz niye uyalım gibi bir yaklaşımınız da olabilir, o da sizin bileceğiniz iş. Benim işime yarıyor, zaten basit bir şey paylaşmak istedim.
C++23, GCC14 işinizi görür daha alt sürümler için uyarlamak size kalmış.
Buffer türü değişebilir, constrainti sağlamanız yeterli ben default TEMP_BUFFER için concept oluşturdum, ihtiyacınıza/kendi API'nize göre düzenlersiniz.
Örnek kullanım
Önemli Not: PacketBuilder buffer'ı chainde `std::forward_like` ile move ediyor. Yani TEMP_BUFFER'ı bu builder ile kullanacaksanız şu konuda bahsettiğim sorunu çözmeniz gerekir: https://forum.turkmmo.com/konu/3950316-temp_buffer-copy-move-problem/
Gözden kaçan bir şey varsa bildirebilirsiniz, düzeltirim.
İyi forumlar.
örnek:
"Neden böyle bir şeye ihtiyacımız olsun?" derseniz, paket gönderirken şu hataları compile-time'da yakalarsınız:
-> padding'li bir struct'ı raw byte olarak alakalı buffer'a yazmak
-> pointer serialize etmeye çalışmak
Contiguous, forward range veya bir POD'u argüman olarak geçebilirsiniz.
Fayda olarak da compile-time validation, type safety sayılabilir. Tabii code-base'in %99u buna uymuyor ki biz niye uyalım gibi bir yaklaşımınız da olabilir, o da sizin bileceğiniz iş. Benim işime yarıyor, zaten basit bir şey paylaşmak istedim.
C++23, GCC14 işinizi görür daha alt sürümler için uyarlamak size kalmış.
Buffer türü değişebilir, constrainti sağlamanız yeterli ben default TEMP_BUFFER için concept oluşturdum, ihtiyacınıza/kendi API'nize göre düzenlersiniz.
Örnek kullanım
C++:
constexpr auto my_send{[](Character ch, std::span<const std::uint8_t> data,
const TEMP_BUFFER& buffer) noexcept
{
ch.print();
for (auto elm : data)
std::cout << static_cast<int>(elm) << '\n';
return 1;
}};
const auto built{
make_builder<my_send>()
.add_payload(payload_bytes)
.add_payload(Payload{.i1 = 1, .i2 = true, .i4 = 2})
.send(ch, payload_bytes)};
const auto pkt{make_builder()
.add_payload(std::uint8_t{42})
.add_payload(std::uint8_t{42})
.add_member_val(&MyPacket::size, 3)
.build_as<MyPacket>()};
if (pkt)
std::cout << pkt->size;
Önemli Not: PacketBuilder buffer'ı chainde `std::forward_like` ile move ediyor. Yani TEMP_BUFFER'ı bu builder ile kullanacaksanız şu konuda bahsettiğim sorunu çözmeniz gerekir: https://forum.turkmmo.com/konu/3950316-temp_buffer-copy-move-problem/
Gözden kaçan bir şey varsa bildirebilirsiniz, düzeltirim.
İyi forumlar.
örnek:
Linkleri görebilmek için Turkmmo Forumuna ÜYE olmanız gerekmektedir.
Son düzenleme:
Trivially-copyable paket struct'larını ve değişken payload'ları bir buffer'a serialize eidp, istediğiniz bir send stratejisiyle gönderen(tabii default-initializable functor/stateless lambda olmak koşuluyla) fluent, move-only bir builder.
"Neden böyle bir şeye ihtiyacımız olsun?" derseniz, paket gönderirken şu hataları compile-time'da yakalarsınız:
-> padding'li bir struct'ı raw byte olarak alakalı buffer'a yazmak
-> pointer serialize etmeye çalışmak
Contiguous, forward range veya bir POD'u argüman olarak geçebilirsiniz.
Fayda olarak da compile-time validation, type safety sayılabilir. Tabii code-base'in %99u buna uymuyor ki biz niye uyalım gibi bir yaklaşımınız da olabilir, o da sizin bileceğiniz iş. Benim işime yarıyor, zaten basit bir şey paylaşmak istedim.
C++23, GCC14 işinizi görür daha alt sürümler için uyarlamak size kalmış.
Buffer türü değişebilir, constrainti sağlamanız yeterli ben default TEMP_BUFFER için concept oluşturdum, ihtiyacınıza/kendi API'nize göre düzenlersiniz.
Örnek kullanım
C++:constexpr auto my_send{[](Character ch, std::span<const std::uint8_t> data, const TEMP_BUFFER& buffer) noexcept { ch.print(); for (auto elm : data) std::cout << static_cast<int>(elm) << '\n'; return 1; }}; const auto built{ make_builder<my_send>() .add_payload(payload_bytes) .add_payload(Payload{.i1 = 1, .i2 = true, .i4 = 2}) .send(ch, payload_bytes)}; const auto pkt{make_builder() .add_payload(std::uint8_t{42}) .add_payload(std::uint8_t{42}) .add_member_val(&MyPacket::size, 3) .build_as<MyPacket>()}; if (pkt) std::cout << pkt->size;
Önemli Not: PacketBuilder buffer'ı chainde `std::forward_like` ile move ediyor. Yani TEMP_BUFFER'ı bu builder ile kullanacaksanız şu konuda bahsettiğim sorunu çözmeniz gerekir: https://forum.turkmmo.com/konu/3950316-temp_buffer-copy-move-problem/
Gözden kaçan bir şey varsa bildirebilirsiniz, düzeltirim.
İyi forumlar.
örnek:Linkleri görebilmek için Turkmmo Forumuna ÜYE olmanız gerekmektedir.
Teşekkürler, fikir olarak bence değerli bir çalışma. Özellikle packet serialize ederken
trivially-copyable olmayan tipleri, pointer yazmayı ve padding ihtimali olan struct'ları
compile-time tarafında engellemeye çalışması gayet mantıklı.
Haklı bulduğum yerler:
- Packet gönderiminde raw byte yazıyorsak type-safety gerçekten önemli.
- Pointer serialize etmeyi compile-time'da engellemek doğru.
- Padding'li struct'ı doğrudan buffer'a basmayı reddetmek doğru bir güvenlik yaklaşımı.
- Dynamic payload yazarken fluent builder mantığı okunabilirlik sağlayabilir.
- Send strategy'nin dışarıdan verilebilmesi güzel; farklı buffer/send API'lerine uyarlama imkanı veriyor.
Ama bana göre bazı noktalar fazla ağır veya riskli:
- C++23, deducing this, std::forward_like ve NTTP lambda kullanımı eski/legacy codebase için bakım maliyetini artırabilir.
Teknik olarak güzel ama ekipte herkesin rahat sürdüreceği bir yapı olmayabilir.
- std::has_unique_object_representations_v<T> padding yakalamak için iyi ama çok katı.
bool, float/double veya bazı platforma bağlı tiplerde beklenmedik şekilde false dönebilir.
Bu güvenli tarafta kalır ama bazı geçerli kullanım senaryolarını da dışarıda bırakabilir.
- Packet içinde std::size_t kullanımı bence network/protocol tarafında doğru değil.
32-bit / 64-bit platforma göre boyutu değişebilir.
Bunun yerine std::uint16_t veya std::uint32_t gibi sabit genişlikli tipler daha sağlıklı olur.
- add_payload(0xFFU) gibi kullanımlar tehlikeli.
Bu 1 byte değil, unsigned int olduğu için genelde 4 byte yazar.
Uyarı notu var ama gerçek projede kolay hata yapılır.
Bence integer literal için add_u8/add_u16/add_u32 gibi açık fonksiyonlar daha güvenli olur.
- Endianness konusu host-endian bırakılmış.
Aynı mimaride sorun olmayabilir ama network/protocol katmanında byte order konusu en azından net bir policy olarak ayrılmalı.
- #pragma pack tarafında örnekte #pragma pop yazılmış.
Doğru kullanım normalde #pragma pack(pop) şeklinde ol#pragma- Bu yapı codebase'in tama#pragmayılırsa fazla soyutlama olabilir.
Metin2 gib#pragmaC++ kodlarında packet struct + static_assert + küçük WritePod helper çoğu yerde daha okunabilir kalır.
Benim şahsi fikrim:
Bu çalışma "gereksiz" değil, tam tersine yakaladığı problem gerçek bir problem.
Ama çözüm biraz modern C++ vitrini gibi olmuş. Core packet sistemini komple buna çevirmek yerine,
yeni packetlerde, dynamic payload yazılan yerlerde veya hata riski yüksek serialize noktalarında
yardımcı güvenli helper olarak kullanmak daha mantıklı olur.
Daha sade bir alternatif yaklaşım şöyle olabilir:
template <typename T>
void WritePod(TEMP_BUFFER& buf, const T& value)
{
static_assert(std::is_trivially_copyable_v<T>);
static_assert(std::has_unique_object_representations_v<T>);
static_assert(!std::is_pointer_v<T>);
buf.write(&value, sizeof(T));
}
inline void WriteU8(TEMP_BUFFER& buf, std::uint8_t v)
{
WritePod(buf, v);
}
inline void WriteU16(TEMP_BUFFER& buf, std::uint16_t v)
{
WritePod(buf, v);
}
inline void WriteU32(TEMP_BUFFER& buf, std::uint32_t v)
{
WritePod(buf, v);
}
Özetle:
Fikir doğru, compile-time validation tarafı faydalı.
Ama size_t, integer literal tuzağı, endianness, platform bağımlılığı ve aşırı modern C++ kullanımı
pratikte dikkat edilmesi gereken taraflar.
Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)
Benzer konular
- Cevaplar
- 35
- Görüntüleme
- 2K