green Да, и так работает. Я же говорю - неудачный пример. Но достаточно посмотреть, скажем, тот же нелюбимый народом boost, чтобы понять, что не всегда все так просто. А вообще, конечно, да, все решается, тут даже спорить не буду.
Плюсы GCC: 1. есть __builtin_expect, в msvc возможен только автоматический PGO, но все тест кейсы при компиляции бывает не прогнать. 2. есть параметризированный инлайн ассемблер, в msvc хоть и есть интринзики на большинство нужных инструкций, но таки не на все. Минусы GCC: 1. нет аналога __assume 2. нет возможности отдельно включить интринзики расширенных инструкций и автогенерацию расширенных инструкций. т.е. если укажем -msse то gcc начнет генерировать sse код автоматически, но и только тогда начнет понимать sse интринзики. Это бред, крайне неудобно делать универсальный код: версии кода для каждого набора инструкций приходится выносить в отдельные сишники и компилировать отдельно с разными ключами. Вот с чем столкнулся я в свое время.
Сам ты школьник. Дядя нашёлся > Ну покажи мне как написать код на встроенном ассемблере, чтобы он собрался MSVC и GCC. И не надо кричать что не пользуйтесь ассемблером. Иногда это очень надо. Под "куча кода" я имел ввиду необходимость писать варианты одного и того же кода для разных компиляторов. Одних опций в makefile не достаточно. Кроме MSVC и GCC вообще говоря есть и другие компиляторы, так что может быть и больше 2 вариантов. Я ж не говорю что для каждого файлы в проекте надо по 2 варианта. По хорошему такие вещи надо в отдельные файлы выносить, чтобы потом править было лечге. Где хоть кто-то писал что это невозможно? Или ты между строк читать умеешь? Это должно быть изначально предусмотрено. Если проект собирается MSVC и GCC, это ещё не значит что он соберётся другим компилятором. Прийдётся некоторый код под него адаптировать. Или ты не согласен?
мда, не ну мда. Это 100% недостаток компиляторов а вообще делаешь обьектник например на фасме, или статическую либу. посмотри любой опенсорц мультиплатформ, там пару файлов с #ifdef -ами, а остальные на переносимом коде. 0_о, действительно все компиляторы и все платформы должны поддерживать Win32API, или все расширения от MS? вообще говоря если писать чист кроскомпил код для одной платформы, то это вообще минимальное количество условий
А кто-то собирался указывать недостатки компиляторов? Это пример кода который ну никак не соберётся разными компиляторами без модификации. При чём здесь WinApi? Я про то что если у тебя будет в исходнике написано #if MSVC //... #elseif GNUC //... #endif и этот код собирыется msvc и gcc, ещё не факт что он соберётся watcom к примеру. Т.е. в исходнике изначально заложен набор компиляторов, чьи расширения поддерживаются. Это насчёт "должно быть предусмотрено". Какое отношение это имеет к WinApi я не понял. Ну так покажи наконец всем пример кода, в котором объявлена структура с выравниванием в 1 байт и который будет собираться любым ANSI C компилятором. В противном случае для каждого компилятора нужен свой код, и при переходе на другой компилятор исходник надо будет модифицировать.
кэп? это дополнение к покажите наконец задачу где это надо, и при этом есть какаето причина еще и код компилировался любым компилятором
cppasm Код, содержащий упакованные структуры, не может быть абсолютно кросплатформенным, потому что возможность доступа к невыровненным данным не гарантируется на всех аппаратных архитектурах (и, как следствие, отсутствует в Стандарте С). Так что дело здесь не в компиляторе.
Да я и не говорю что дело в компиляторе. Да, у разных архитектур разные требования к выравниванию. И тем не менее бывают случаи (и не редко) когда выравнивание всё-таки надо задать. И именно из-за того что средства отсутствуют в стандарте (по вполне понятным причинам), прийдётся писать разные варианты для разных компиляторов. Я не про то, что компиляторы плохие. Я про то, что невозможно написать код, который бы вообще без изменений компилировался любым компилятором под любую платформу. Кроме самых примитивных случаев, хоть минимальные изменения вносить прийдётся.
cppasm Компилятор может в принципе не поддерживать выравнивание, даже в качестве расширения Стандарта. Так что для полной кросплатформенности придётся всё-таки обойтись без выравнивания.
SadKo мы про что и толкуем, что там 5 строк кода будет, не юзают же постоянно выравнивания ,и вообще можно и без них обойтись. Я не могу придумать задачу где без них ну вообще не как
SPA задача такая - есть сетевой протокол где жестко задана структура пакета, надо принимать\отправлять эти пакеты хорошее решение - использовать для описания пакетов соответствующий POD-тип с выравниванием 1 плохое решение - использовать код вида "short x=*(short*)buf; buf+=2;"
GoldFinch Ни-то и ни другое, хорошее решение это использовать серилизатор независимый от бинарной организации.
Как вариант, а использовать структуру как раз решение не из лучших Код (Text): /* * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef _BYTEBUFFER_H #define _BYTEBUFFER_H #include "Common.h" #include "Errors.h" #include "Log.h" #include "Utilities/ByteConverter.h" class ByteBufferException { public: ByteBufferException(bool _add, size_t _pos, size_t _esize, size_t _size) : add(_add), pos(_pos), esize(_esize), size(_size) { PrintPosError(); } void PrintPosError() const { sLog.outError("ERROR: Attempted to %s in ByteBuffer (pos: " SIZEFMTD " size: "SIZEFMTD") value with size: " SIZEFMTD, (add ? "put" : "get"), pos, size, esize); } private: bool add; size_t pos; size_t esize; size_t size; }; class ByteBuffer { public: const static size_t DEFAULT_SIZE = 0x1000; // constructor ByteBuffer(): _rpos(0), _wpos(0) { _storage.reserve(DEFAULT_SIZE); } // constructor ByteBuffer(size_t res): _rpos(0), _wpos(0) { _storage.reserve(res); } // copy constructor ByteBuffer(const ByteBuffer &buf): _rpos(buf._rpos), _wpos(buf._wpos), _storage(buf._storage) { } void clear() { _storage.clear(); _rpos = _wpos = 0; } template <typename T> void append(T value) { EndianConvert(value); append((uint8 *)&value, sizeof(value)); } template <typename T> void put(size_t pos,T value) { EndianConvert(value); put(pos,(uint8 *)&value,sizeof(value)); } ByteBuffer &operator<<(uint8 value) { append<uint8>(value); return *this; } ByteBuffer &operator<<(uint16 value) { append<uint16>(value); return *this; } ByteBuffer &operator<<(uint32 value) { append<uint32>(value); return *this; } ByteBuffer &operator<<(uint64 value) { append<uint64>(value); return *this; } // signed as in 2e complement ByteBuffer &operator<<(int8 value) { append<int8>(value); return *this; } ByteBuffer &operator<<(int16 value) { append<int16>(value); return *this; } ByteBuffer &operator<<(int32 value) { append<int32>(value); return *this; } ByteBuffer &operator<<(int64 value) { append<int64>(value); return *this; } // floating points ByteBuffer &operator<<(float value) { append<float>(value); return *this; } ByteBuffer &operator<<(double value) { append<double>(value); return *this; } ByteBuffer &operator<<(const std::string &value) { append((uint8 const *)value.c_str(), value.length()); append((uint8)0); return *this; } ByteBuffer &operator<<(const char *str) { append((uint8 const *)str, str ? strlen(str) : 0); append((uint8)0); return *this; } ByteBuffer &operator>>(bool &value) { value = read<char>() > 0 ? true : false; return *this; } ByteBuffer &operator>>(uint8 &value) { value = read<uint8>(); return *this; } ByteBuffer &operator>>(uint16 &value) { value = read<uint16>(); return *this; } ByteBuffer &operator>>(uint32 &value) { value = read<uint32>(); return *this; } ByteBuffer &operator>>(uint64 &value) { value = read<uint64>(); return *this; } //signed as in 2e complement ByteBuffer &operator>>(int8 &value) { value = read<int8>(); return *this; } ByteBuffer &operator>>(int16 &value) { value = read<int16>(); return *this; } ByteBuffer &operator>>(int32 &value) { value = read<int32>(); return *this; } ByteBuffer &operator>>(int64 &value) { value = read<int64>(); return *this; } ByteBuffer &operator>>(float &value) { value = read<float>(); return *this; } ByteBuffer &operator>>(double &value) { value = read<double>(); return *this; } ByteBuffer &operator>>(std::string& value) { value.clear(); while (rpos() < size()) // prevent crash at wrong string format in packet { char c = read<char>(); if (c == 0) break; value += c; } return *this; } uint8 operator[](size_t pos) const { return read<uint8>(pos); } size_t rpos() const { return _rpos; } size_t rpos(size_t rpos_) { _rpos = rpos_; return _rpos; } size_t wpos() const { return _wpos; } size_t wpos(size_t wpos_) { _wpos = wpos_; return _wpos; } template<typename T> void read_skip() { read_skip(sizeof(T)); } void read_skip(size_t skip) { if(_rpos + skip > size()) throw ByteBufferException(false, _rpos, skip, size()); _rpos += skip; } template <typename T> T read() { T r = read<T>(_rpos); _rpos += sizeof(T); return r; } template <typename T> T read(size_t pos) const { if(pos + sizeof(T) > size()) throw ByteBufferException(false, pos, sizeof(T), size()); T val = *((T const*)&_storage[pos]); EndianConvert(val); return val; } void read(uint8 *dest, size_t len) { if(_rpos + len > size()) throw ByteBufferException(false, _rpos, len, size()); memcpy(dest, &_storage[_rpos], len); _rpos += len; } bool readPackGUID(uint64& guid) { if(rpos() + 1 > size()) return false; guid = 0; uint8 guidmark = 0; (*this) >> guidmark; for(int i = 0; i < 8; ++i) { if(guidmark & (uint8(1) << i)) { if(rpos() + 1 > size()) return false; uint8 bit; (*this) >> bit; guid |= (uint64(bit) << (i * 8)); } } return true; } const uint8 *contents() const { return &_storage[0]; } size_t size() const { return _storage.size(); } bool empty() const { return _storage.empty(); } void resize(size_t newsize) { _storage.resize(newsize); _rpos = 0; _wpos = size(); } void reserve(size_t ressize) { if (ressize > size()) _storage.reserve(ressize); } void append(const std::string& str) { append((uint8 const*)str.c_str(), str.size() + 1); } void append(const char *src, size_t cnt) { return append((const uint8 *)src, cnt); } template<class T> void append(const T *src, size_t cnt) { return append((const uint8 *)src, cnt * sizeof(T)); } void append(const uint8 *src, size_t cnt) { if (!cnt) return; ASSERT(size() < 10000000); if (_storage.size() < _wpos + cnt) _storage.resize(_wpos + cnt); memcpy(&_storage[_wpos], src, cnt); _wpos += cnt; } void append(const ByteBuffer& buffer) { if(buffer.wpos()) append(buffer.contents(), buffer.wpos()); } // can be used in SMSG_MONSTER_MOVE opcode void appendPackXYZ(float x, float y, float z) { uint32 packed = 0; packed |= ((int)(x / 0.25f) & 0x7FF); packed |= ((int)(y / 0.25f) & 0x7FF) << 11; packed |= ((int)(z / 0.25f) & 0x3FF) << 22; *this << packed; } void appendPackGUID(uint64 guid) { if (_storage.size() < _wpos + sizeof(guid) + 1) _storage.resize(_wpos + sizeof(guid) + 1); size_t mask_position = wpos(); *this << uint8(0); for(uint8 i = 0; i < 8; ++i) { if(guid & 0xFF) { _storage[mask_position] |= uint8(1 << i); *this << uint8(guid & 0xFF); } guid >>= 8; } } void put(size_t pos, const uint8 *src, size_t cnt) { if(pos + cnt > size()) throw ByteBufferException(true, pos, cnt, size()); memcpy(&_storage[pos], src, cnt); } void print_storage() const { if(!sLog.IsOutDebug()) // optimize disabled debug output return; sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() ); for(uint32 i = 0; i < size(); ++i) sLog.outDebugInLine("%u - ", read<uint8>(i) ); sLog.outDebug(" "); } void textlike() const { if(!sLog.IsOutDebug()) // optimize disabled debug output return; sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() ); for(uint32 i = 0; i < size(); ++i) sLog.outDebugInLine("%c", read<uint8>(i) ); sLog.outDebug(" "); } void hexlike() const { if(!sLog.IsOutDebug()) // optimize disabled debug output return; uint32 j = 1, k = 1; sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() ); if(sLog.IsIncludeTime()) sLog.outDebugInLine(" "); for(uint32 i = 0; i < size(); ++i) { if ((i == (j * 8)) && ((i != (k * 16)))) { if (read<uint8>(i) < 0x10) { sLog.outDebugInLine("| 0%X ", read<uint8>(i) ); } else { sLog.outDebugInLine("| %X ", read<uint8>(i) ); } ++j; } else if (i == (k * 16)) { if (read<uint8>(i) < 0x10) { sLog.outDebugInLine("\n"); if(sLog.IsIncludeTime()) sLog.outDebugInLine(" "); sLog.outDebugInLine("0%X ", read<uint8>(i) ); } else { sLog.outDebugInLine("\n"); if(sLog.IsIncludeTime()) sLog.outDebugInLine(" "); sLog.outDebugInLine("%X ", read<uint8>(i) ); } ++k; ++j; } else { if (read<uint8>(i) < 0x10) { sLog.outDebugInLine("0%X ", read<uint8>(i) ); } else { sLog.outDebugInLine("%X ", read<uint8>(i) ); } } } sLog.outDebugInLine("\n"); } protected: size_t _rpos, _wpos; std::vector<uint8> _storage; }; template <typename T> inline ByteBuffer &operator<<(ByteBuffer &b, std::vector<T> v) { b << (uint32)v.size(); for (typename std::vector<T>::iterator i = v.begin(); i != v.end(); ++i) { b << *i; } return b; } template <typename T> inline ByteBuffer &operator>>(ByteBuffer &b, std::vector<T> &v) { uint32 vsize; b >> vsize; v.clear(); while(vsize--) { T t; b >> t; v.push_back(t); } return b; } template <typename T> inline ByteBuffer &operator<<(ByteBuffer &b, std::list<T> v) { b << (uint32)v.size(); for (typename std::list<T>::iterator i = v.begin(); i != v.end(); ++i) { b << *i; } return b; } template <typename T> inline ByteBuffer &operator>>(ByteBuffer &b, std::list<T> &v) { uint32 vsize; b >> vsize; v.clear(); while(vsize--) { T t; b >> t; v.push_back(t); } return b; } template <typename K, typename V> inline ByteBuffer &operator<<(ByteBuffer &b, std::map<K, V> &m) { b << (uint32)m.size(); for (typename std::map<K, V>::iterator i = m.begin(); i != m.end(); ++i) { b << i->first << i->second; } return b; } template <typename K, typename V> inline ByteBuffer &operator>>(ByteBuffer &b, std::map<K, V> &m) { uint32 msize; b >> msize; m.clear(); while(msize--) { K k; V v; b >> k >> v; m.insert(make_pair(k, v)); } return b; } template<> inline void ByteBuffer::read_skip<char*>() { std::string temp; *this >> temp; } template<> inline void ByteBuffer::read_skip<char const*>() { read_skip<char*>(); } template<> inline void ByteBuffer::read_skip<std::string>() { read_skip<char*>(); } #endif
SPA вот это копипасты там.... почему не шаблоны? Booster сериализатор копирующий структуру с выравниванием 1 в структуру с другим выравниванием -это оверхед по памяти и по времени выполнения также для сериализатора надо написать в два раза больше кода: перечислить поля в смой структуре, и еще раз перечислить их в функции serialize()
Вы для начала определитесь о каком языке речь. Если речь о Си - все сериализаторы с шаблонами идут лесом, если о плюсах, тогда другой разговор. Любая связь с внешним миром. Например ОС Api хочет указатель на структуру с определённым выравниванием как параметр. Что в таком случае делать?