Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Как Вы обычно передаете stream разрабатывая парсер


Автор: EvilsInterrupt 22.6.2013, 13:57
При разработке класса по работе с каким-либо форматом файла мы часто пользуемся стримами чтобы читать\писать из\в файл. До сих пор использую способ передачи базового класса стрима по ссылке в конструктор класса. Мне перестает нравиться по разным причинам и хочу узнать опыт других разработчиков, послушать их идиомы и методы.

Чтобы не было слишком излишне абстрактно возьмем задачу по разработке класса по работе с форматом "Super Format", ниже в коде приведены декларации заголовков. Также давайте напишем, который должен хранить и возвращать эти заголовки пользовательскому коду.

Код

namespace SuperFormat
{


struct FirstHeader
{
    uint8_t   magic;
    uint32_t  secondHeaderOffset;
};

struct SecondHeader
{
    uint16_t magic;
};


class SFImage
{
public:
    typedef  std::list<SecondHeader>  SecondHeaders_t;

public:
    const FirstHeader& first() const
    {
        return firstHeader;
    }

    const SecondHeaders_t& seconds() const
    {
        return secondHeaders;
    }

private:
    FirstHeader firstHeader;
    SecondHeaders_t secondHeaders;
};


} // namespace SuperFormat



Задача: Как-либо передать данные из\ в стрим во внутренние приватные члены firstHeader и secondHeaders. Другими словами как организовать работу со стримом, чтобы писать и записывать данные в эти приватные заголовки?

Пока вижу такие варианты:

Код

    // [1]
    SuperFormat::SFImage image;
    image.read( stream );
    image.first().magic = 0x84;
    image.write( stream );

    // [2]
    SuperFormat::SFImage image;
    read( image, stream );
    image.first().magic = 0x84;
    write( image, stream );

    // [3]
    SuperFormat::SFImage image(stream);
    image.read();
    image.first().magic = 0x84;
    image.write();

    // [4]
    SuperFormat::SFImage image;
    StreamPtr stream( new MemoryStream( 1000 * Mb) );
    image.setStream(stream);
    image.read();
    image.first().magic = 0x84;
    image.write();


Возможно Вы предложите еще, не стесняйтесь )

Автор: mes 22.6.2013, 18:37
Цитата(EvilsInterrupt @  22.6.2013,  12:57 Найти цитируемый пост)
Возможно Вы предложите еще, не стесняйтесь )


stream<<any_format(image)

Автор: EvilsInterrupt 22.6.2013, 19:27
Цитата(mes @  22.6.2013,  19:37 Найти цитируемый пост)
stream<<any_format(image)

Я отказался от такого варианта, т.к. менее читабелен чем read()/write() .

Автор: volatile 22.6.2013, 20:01
1,
Цитата(EvilsInterrupt @  22.6.2013,  19:27 Найти цитируемый пост)
 менее читабелен чем read()/write() . 

как это менее читабелен?
Это же устоявшаяся практика в С++...

2.
Цитата(EvilsInterrupt @  22.6.2013,  13:57 Найти цитируемый пост)
    const FirstHeader& first() const

Цитата(EvilsInterrupt @  22.6.2013,  13:57 Найти цитируемый пост)
  image.first().magic = 0x84;

Как это вы пишете по константной ссылке?
И развивая мысль, если же возвращать не контантную ссылку, то смысла делать члены приватными - лишняя трата времени. все равно у всех полный доступ  smile 

3.
В общем в С++, есть устоявшаяся практика, делать глобальный оператор<<, и объявлять его дружественным (friend).
для доступа непосредственно к приватным членам.

Все остальные модификации  (по крайней мере для вашего примера) - суть одно и тоже, записанное разными способами.
Так зачем голову морочить себе и другим? 
Лучше идти обычным путем.


Автор: EvilsInterrupt 22.6.2013, 20:57
Цитата(volatile @  22.6.2013,  21:01 Найти цитируемый пост)
делать глобальный оператор<<, и объявлять его дружественным (friend).

Ввиду того  что пока у меня сформировалось свое собственное мнение по поводу использования "друзей", стараюсь этого не делать smile

Добавлено через 9 минут и 34 секунды
Цитата(volatile @  22.6.2013,  21:01 Найти цитируемый пост)
как это менее читабелен?
Это же устоявшаяся практика в С++...

Не совсем. Попадавшиеся мне проекты с применением набора библиотек POCO ни одного перегруженного оператора вывода\ввода. Правда это были проекты продуктов для reverse-engineer-ов и возможно это особенность мышления разработчиков. Но тем не менее не везде перегружается. К примеру в продукте компании где работаю не применяется

Автор: volatile 22.6.2013, 21:21
Цитата(EvilsInterrupt @  22.6.2013,  20:57 Найти цитируемый пост)
К примеру в продукте компании где работаю не применяется 

EvilsInterrupt, ну хорошо, делайте как принято у вас.
Принциаиальной выгоды от какого-то определенного способа нет.
Главное здесь - делать однообразно, а не придумывать каждый раз новое седло к старому велосипеду.  smile 



Автор: EvilsInterrupt 22.6.2013, 21:33
Цитата(volatile @  22.6.2013,  22:21 Найти цитируемый пост)
ну хорошо, делайте как принято у вас.

Вопрос не в том делать мне или не делать как у меня принято в компании. Вопрос в другом.

Цитата(volatile @  22.6.2013,  22:21 Найти цитируемый пост)
Принциаиальной выгоды от какого-то определенного способа нет.

Есть. Столкнулся с тем, что мне стало сложно добавить новый код. У меня класс по работе с форматом принимает ссылку в конструкторе. Это приводит к тому, что после класс заполнить данными из другого стрима уже невозможно. Сейчас приходится рефакторить, вот если бы кто подсказал ранее!!! Да в процессе рефакторинга, сначала передавал стрим с помощью boost::shared_ptr, но это оказалось излишним. Вот и хочется узнать кто как делает, чтобы уменьшить количество лишних телодвижений.


Автор: volatile 23.6.2013, 00:26
Цитата(EvilsInterrupt @  22.6.2013,  21:33 Найти цитируемый пост)
Это приводит к тому, что после класс заполнить данными из другого стрима уже невозможно

Вот кстати, именно поэтому и не нужно изобретать велосипеды.  smile 


Автор: mes 23.6.2013, 03:14
Цитата(EvilsInterrupt @  22.6.2013,  18:27 Найти цитируемый пост)
Я отказался от такого варианта, т.к. менее читабелен чем read()/write() . 

 smile Вы только разницу в синтаксисе заметили ? запишите написанное со скобками, сути не изменится.. 
подсказка: в моем примере трое участников вместо двух.. 

Цитата(EvilsInterrupt @  22.6.2013,  19:57 Найти цитируемый пост)
Код

делать глобальный оператор<<, и объявлять его дружественным (friend).
Ввиду того  что пока у меня сформировалось свое собственное мнение по поводу использования "друзей", стараюсь этого не делать 

нет прямой связи друзей с применением оператора.. Кстати чем так сильно отличается дружественная функция, от обычного метода ?!

Добавлено @ 03:24
Цитата(EvilsInterrupt @  22.6.2013,  20:33 Найти цитируемый пост)
 Это приводит к тому, что после класс заполнить данными из другого стрима уже невозможно. Сейчас приходится рефакторить, вот если бы кто подсказал ранее!!!


так вроде ж: http://forum.vingrad.ru/forum/topic-336639/unread-1.html  smile 

Автор: EvilsInterrupt 23.6.2013, 08:51
Цитата(mes @  23.6.2013,  04:14 Найти цитируемый пост)
так вроде ж: http://forum.vingrad.ru/forum/topic-336639/unread-1.html 

Это был формат PE, а я сейчас ковыряюсь с другим форматом, хоть и не хочется, но трудящиеся просят )

Цитата(mes @  23.6.2013,  04:14 Найти цитируемый пост)
Кстати чем так сильно отличается дружественная функция, от обычного метода ?!

Дело в том, что в public попадает то что очень редко будет меняться, а в привате автор кода имеет право переколбасить в любой момент наплевав на любое мнение. Если же сделать дружественную функцию, то в ней возникнет соблазн заюзать "по-быстрому" вон-ту функцию, но возникает лишняя зависимость, которую приходится учитывать если автор кода поменял сигнатуру исопользуемой функции или вообще удалил. Все-таки паблик на то и паблик, чтобы сказать пользователям класса "Вот методы, которые постараюсь менять в самых экстренных случаях"

Автор: volatile 23.6.2013, 09:04
Цитата(EvilsInterrupt @  23.6.2013,  08:51 Найти цитируемый пост)
 же сделать дружественную функцию, то в ней возникнет соблазн заюзать 

Ну запишите эту дружественную функцию в самом определении класа. (часто так и делают, кстати.)
И считайте что она не дружественная, а обычный метод.
Проблема, имхо, у вас в голове.

Автор: EvilsInterrupt 23.6.2013, 11:40
Цитата(volatile @  23.6.2013,  10:04 Найти цитируемый пост)
Проблема, имхо, у вас в голове. 

Тут Вы правы из-за желания написать хорошо иногда вхожу в ступор, вместо того чтобы просто написать и посмотреть как оно на практике будет выглядеть.

volatile, 
Попробую показать свое понимание дружеских функций, возможно вы увидите ошибки в рассуждениях и укажите на них. Сразу скажу, что опыт применения дружеских функций маленький и поэтому вполне возможно что вы все-таки ошибки увидите! Далее следуют мои мысли, а не цитаты из учебников, поэтому не следует говорить "где же вы такого по-набирались?".

Чисто технически пользовательский код не может видеть приватную часть другого класса. Но это можно организовать объявив в объявлении класса эту функцию дружественной. Получается вроде входит в namespace класса и в тоже время видит все его внутреннее состояние словно это метод класса. Однако при реализации такой функции нужны лишь часть а не весь функционал класса и получается своего рода "дыра". Во время написании такой функции программист отчетливо понимает как и что он пишет и зачем. Но проходит время, к этой функции обращается уже другой программист, который не знает всех деталей и нюансов класса. Который между прочим за это время тоже мог измениться. Для решения задачи из-за чего пришлось обратиться к модификации этой дружественной функции программист может обратиться к тому функционалу класса, который лучше всего не трогать! Да, спустя время юнит-тесты, интеграционные тесты или гневный пользователь все-таки ему скажет о неправильности его действий, но ведь это время!

По этой причине я делю код на интерфейсный и детали. Детали я изменяю как хочу и когда хочу, никто мне в этом ни указ, главное чтобы работало! А вот интерфейсный стараюсь не менять, иначе пользовательский код придется менять. Очень жалею что нету C++ конструкции, чтобы пометить какой-либо интерфейсный код меткой "depricated", разве что pragma для MSVS но это не портабельно!

Т.е. мое отношение к дружественным функциям такое, что лучше избегать их, иначе кто-то заюзает детали класса, а тебе потом собирать "артефакты" или помогать пофиксить код.

Автор: mes 23.6.2013, 12:36
Цитата(EvilsInterrupt @  23.6.2013,  07:51 Найти цитируемый пост)
Это был формат PE, а я сейчас ковыряюсь с другим форматом

и  smile  что это меняет ?!  в той теме были обобщенные советы, а не привязанные к формату.. 


Цитата(EvilsInterrupt @  23.6.2013,  10:40 Найти цитируемый пост)
Попробую показать свое понимание дружеских функций, возможно вы увидите ошибки в рассуждениях и укажите на них. 


Цитата(EvilsInterrupt @  23.6.2013,  10:40 Найти цитируемый пост)
Для решения задачи из-за чего пришлось обратиться к модификации этой дружественной функции программист может обратиться к тому функционалу класса, который лучше всего не трогать! 

все это относится и к методу... 

Цитата(EvilsInterrupt @  23.6.2013,  10:40 Найти цитируемый пост)
По этой причине я делю код на интерфейсный и детали. Детали я изменяю как хочу и когда хочу, никто мне в этом ни указ, главное чтобы работало! А вот интерфейсный стараюсь не менять, иначе пользовательский код придется менять. 

1. операторы относятся тоже к интерфейсу
2. где тут проблема с дружественными функциями ?!
3. оператор << может быть вполне и не дружественным.. 

вместо подобных рассуждений, думаю не плохо было бы задать себе вопрос, в чем же прелесть дружественных функций, и сразу станет понятно когда стоит ич применять, а когда нет..

Добавлено через 2 минуты и 31 секунду
Цитата(EvilsInterrupt @  23.6.2013,  10:40 Найти цитируемый пост)
Т.е. мое отношение к дружественным функциям такое, что лучше избегать их,

конечно, если от их применения ни красоты, ни эффективности не добавляется...

Добавлено через 4 минуты и 14 секунд
Цитата(volatile @  23.6.2013,  08:04 Найти цитируемый пост)
Ну запишите эту дружественную функцию в самом определении класа.

EvilsInterrupt, или сделайте метод вывода в поток, и заюзайте его в операторе - будут и овцы сыты и волки целы-  smile

Добавлено через 9 минут и 32 секунды
Цитата(EvilsInterrupt @  23.6.2013,  07:51 Найти цитируемый пост)
 Все-таки паблик на то и паблик, чтобы сказать пользователям класса "Вот методы, которые постараюсь менять в самых экстренных случаях"

1. public методы могут быть у private-классов.. smile.. 
2. интерфейс бывает и protected, и на него распространяются те же требования..

Добавлено через 11 минут и 48 секунд
Цитата(EvilsInterrupt @  23.6.2013,  07:51 Найти цитируемый пост)
, но возникает лишняя зависимость, 

это вобще отдельная тема, гораздо шире, чем публик-методы.. и кстати дружественные функции как раз и придуманы для уменьшения зависимостей и укрепления интерфейса  smile 

Автор: EvilsInterrupt 23.6.2013, 12:53
Цитата(mes @  23.6.2013,  13:36 Найти цитируемый пост)
вместо подобных рассуждений, думаю не плохо было бы задать себе вопрос, в чем же прелесть дружественных функций, и сразу станет понятно когда стоит ич применять, а когда нет..

Эти рассуждения как раз и были высказаны, чтобы ответить на этот вопрос. Я не однократно задавался этим вопросом и не понимаю в чем их крутость? Однако имеется только одно пояснение, которое я нашел зачем эти дружественные были введены в язык и оно написано в книге Страуструпа про Дизайн и Эволюцию языка.

Ок, спрошу у Вас: как вы понимаете в чем прелесть возможности объявить функцию\метод дружественной?

Автор: mes 23.6.2013, 12:58
Цитата(EvilsInterrupt @  23.6.2013,  11:53 Найти цитируемый пост)
 в чем прелесть возможности объявить функцию\метод дружественной?

1. в том, что родной класс может быть не первым аргументом (this), уступив это место чужому
2. в том, что  можно связать два класса, без лишней публикации деталей.

Автор: mes 23.6.2013, 13:16
Цитата(EvilsInterrupt @  23.6.2013,  11:53 Найти цитируемый пост)
 Однако имеется только одно пояснение, которое я нашел зачем эти дружественные были введены в язык и оно написано в книге Страуструпа про Дизайн и Эволюцию языка.

3.6.2 ?  оно и есть  smile 

Автор: EvilsInterrupt 23.6.2013, 13:23
mes, Да, оно самое! ;) Кроме него больше не видел ни одного пояснения. Но то что приведено, на столько мало. Что невозможно сразу сформировать свое мнение о дружественных. Приходится ждать появления опыта и отказаться до момента его появления )

Автор: mes 23.6.2013, 13:54
Цитата(EvilsInterrupt @  23.6.2013,  12:23 Найти цитируемый пост)
 Но то что приведено, на столько мало. Что невозможно сразу сформировать свое мнение о дружественных

тот же метод, но снаружи класса, что позволяет больше свобод при перегрузке.. 
Проблема понимания наверняка заключается в том, что еше не осознали прелесть перегрузки...  smile 

Автор: volatile 23.6.2013, 13:55
Цитата(EvilsInterrupt @  23.6.2013,  11:40 Найти цитируемый пост)
Чисто технически пользовательский код не может видеть приватную часть другого класса. Но это можно организовать объявив в объявлении класса эту функцию дружественной.

Ваша ошибка в понимании дружественных ф-ий, имхо в том что  вы считаете что дружественная функция имеет отношение к пользовательскому коду.
Дружественая функция - это продолжение класса.
Можно считать их теми-же методами, только с другим синтаксисом вызова. Вот и все.



Автор: EvilsInterrupt 23.6.2013, 13:58
Цитата(volatile @  23.6.2013,  14:55 Найти цитируемый пост)
в том что  вы считаете что дружественная функция имеет отношение к пользовательскому коду.

Да, именно так и считаю. А как иначе если она видна пользовательскому коду и не спрятана в private и namespace detailed ?

Автор: mes 23.6.2013, 14:00
Цитата(EvilsInterrupt @  23.6.2013,  12:58 Найти цитируемый пост)
 А как иначе если она видна пользовательскому коду

Вы путатете понятия "интерфейс (библиотеки)" и "пользовательский код"

Добавлено через 45 секунд
для пользователя все равно, дружественная ли функция, или нет...

Автор: EvilsInterrupt 23.6.2013, 14:21
Судя по всему, собравшиеся mes и volatile предпочли бы такой вариант:

Код

namespace SuperFormat
{


struct FirstHeader_t
{
    uint8_t   Magic;
    uint32_t  SecondHeaderOffset;
};

struct SecondHeader_t
{
    uint16_t Magic;
};


class SFImage
{
public:
    typedef  std::list<SecondHeader_t>  SecondHeaders_t;

public:
    friend  void read( std::istream& stream, SFImage* image );
    friend  void write( std::ostream& stream, SFImage& image );

public:
    FirstHeader_t   FirstHeader;
    SecondHeaders_t SecondHeaders;

private:

    void readFirstHeader( std::istream& stream )
    {
        char * ptr = reinterpret_cast<char*>(&FirstHeader);
        std::streamsize sz = sizeof(FirstHeader);
        stream.read( ptr, sz );
    }

    void readSecondHeader( std::istream& stream )
    {
        stream.seekg(FirstHeader.SecondHeaderOffset);
        SecondHeader_t header;
        header.Magic = 0xFF;
        while (header.Magic != 0)
        {
            char * ptr = reinterpret_cast<char *>(&header);
            std::streamsize sz = sizeof(SecondHeader_t);
            stream.read( ptr, sz );
            SecondHeaders.push_back(header);
        }
    }

    void writeFirstHeader( std::ostream& stream )
    {
        const char * ptr = reinterpret_cast<const char*>(&FirstHeader);
        std::streamsize sz = static_cast<std::streamsize>( sizeof(FirstHeader) );
        stream.write( ptr, sz );
    }

    void writeSecondHeader( std::ostream& stream )
    {
        typedef  SecondHeaders_t::const_iterator  HeaderIter_type;
        HeaderIter_type it = SecondHeaders.begin();
        while ( it != SecondHeaders.end() )
        {
            char * ptr = reinterpret_cast<char *>( &it );
            std::streamsize sz = sizeof(SecondHeader_t);
            stream.write( ptr, sz );
        }
    }
};



void read( std::istream& stream, SFImage* image )
{
    std::streampos pos = stream.tellg();
    image->readFirstHeader(stream);
    image->readSecondHeader(stream);
    stream.seekg(pos);
}

void write( std::ostream& stream, SFImage& image )
{
    std::streampos pos = stream.tellp();
    image.writeFirstHeader(stream);
    image.writeSecondHeader(stream);
    stream.seekp(pos);
}



template<typename char_type, typename char_traits>
inline
std::basic_istream<char_type, char_traits>&
operator >> ( std::basic_istream<char_type, char_traits>& stream, SFImage& image )
{
    read( stream, &image );
    return stream;
}

template<typename char_type, typename char_traits>
inline
std::basic_ostream<char_type, char_traits>& 
operator << ( std::basic_ostream<char_type, char_traits>& stream, SFImage& image )
{
    write( stream, image );
    return stream;
}


} // namespace SuperFormat


Верно?

Автор: mes 23.6.2013, 15:11
Цитата(EvilsInterrupt @  23.6.2013,  13:21 Найти цитируемый пост)
собравшиеся mes и volatile предпочли бы такой вариант: 

от лица mes : не совсем или даже совсем не.. 

Автор: EvilsInterrupt 23.6.2013, 16:58
Цитата(mes @  23.6.2013,  16:11 Найти цитируемый пост)
от лица mes : не совсем или даже совсем не.. 

Ок, а какой тогда код был бы? Задача маленькая, думаю займет у вас не более 5 мин. ;)

Автор: mes 23.6.2013, 17:31
для приведенного куска, аддптировано под write/read smile

Код

namespace SuperFormat
{
struct FirstHeader_t
{
    uint8_t   Magic;
    uint32_t  SecondHeaderOffset;
};
struct SecondHeader_t
{
    uint16_t Magic;
};

typedef  std::list<SecondHeader_t>  SecondHeaders_t;

struct SFImage
{
    FirstHeader_t   FirstHeader;
    SecondHeaders_t SecondHeaders;
};

void write(.., FirstHeader_t const&) {..}
void write(.., SecondHeader_t const&) {..}
void write(.., SFImage const& img)
{
    write(.., img->FirstHeader);
     for (...) write(.., img->SecondHeaders[..]);
}
//также для read

} // namespace SuperFormat

Автор: EvilsInterrupt 23.6.2013, 17:41
mes, 
Если не секрет, то почему так? 

Я видел подобный способ на http://stackoverflow.com/ , но меня он отталкиваем тем что похож на процедурный подход.

Автор: mes 23.6.2013, 18:06
Цитата(EvilsInterrupt @  23.6.2013,  16:41 Найти цитируемый пост)
 похож на процедурный подход

а вы наверное считаете, что запихнув все в классы получите ооп-подход smile 
замените read/write на операторы и будет вам счастие smile
 
А по сути в вашем примере чрезмерная инкапсуляция, которая прячет важные сущности, не принося никакой пользы этим, зато сложностей и зависимостей добавляет кучу smile

Добавлено через 10 минут и 8 секунд
Цитата(EvilsInterrupt @  23.6.2013,  16:41 Найти цитируемый пост)
 на процедурный подход.

и да : крылья...ноги...главное хвост! ©

Автор: EvilsInterrupt 23.6.2013, 18:19
mes, 
Приведу твой код, тут:
Код

namespace SuperFormat
{


struct FirstHeader_t
{
    uint8_t   Magic;
    uint32_t  SecondHeaderOffset;
};

struct SecondHeader_t
{
    uint16_t Magic;
};

typedef  std::list<SecondHeader_t>  SecondHeaders_t;


struct SFImage
{
    FirstHeader_t   FirstHeader;
    SecondHeaders_t SecondHeaders;
};


template<typename Header_type>
void read( std::istream& stream, Header_type * header )
{
    char * ptr = reinterpret_cast<char*>(header);
    std::streamsize sz = sizeof(*header);
    stream.read( ptr, sz );
}

template<typename Header_type>
void write( std::ostream& stream, const Header_type& header )
{
    const char * ptr = reinterpret_cast<const char *>( &header );
    std::streamsize sz = sizeof(header);
    stream.write( ptr, sz );
}


void read( std::istream& stream, SFImage* image )
{
    std::streampos pos = stream.tellg();

    stream.seekg(0);
    read(stream, &image->FirstHeader );

    stream.seekg(image->FirstHeader.SecondHeaderOffset);

    SecondHeader_t header;
    header.Magic = 0xFF;
    while (header.Magic != 0)
    {
        read(stream, &header );
        image->SecondHeaders.push_back(header);
    }

    stream.seekg(pos);
}

void write( std::ostream& stream, SFImage& image )
{
    std::streampos pos = stream.tellp();

    stream.seekp(0);
    write(stream, image.FirstHeader );
    stream.seekp(image.FirstHeader.SecondHeaderOffset );

    typedef  SecondHeaders_t::const_iterator  HeaderIter_type;
    HeaderIter_type it = image.SecondHeaders.begin();
    HeaderIter_type end = image.SecondHeaders.end();
    while ( it != end )
    {
        write( stream, *it );
        ++it;
    }

    stream.seekp(pos);
}



template<typename char_type, typename char_traits>
inline
    std::basic_istream<char_type, char_traits>&
    operator >> ( std::basic_istream<char_type, char_traits>& stream, SFImage& image )
{
    read( stream, &image );
    return stream;
}

template<typename char_type, typename char_traits>
inline
    std::basic_ostream<char_type, char_traits>& 
    operator << ( std::basic_ostream<char_type, char_traits>& stream, SFImage& image )
{
    write( stream, image );
    return stream;
}


} // namespace SuperFormat


все верно? ;)

Автор: mes 23.6.2013, 18:51
Цитата(EvilsInterrupt @  23.6.2013,  17:19 Найти цитируемый пост)
все верно? ;) 

наполовину... 

Цитата(EvilsInterrupt @  23.6.2013,  17:19 Найти цитируемый пост)
  write(stream, image.FirstHeader );
    stream.seekp(image.FirstHeader.SecondHeaderOffset );

это использование не по назначению потока (mistreatment).. Такое можно проделывать со своим потоком, в котором Вы уверены.. Но оставлять такое поведение пользователю недопустимо.. Это место требует обязательной разгрузки через сущность..

Добавлено @ 18:56
т.е. сами header`ы у Вас вполне потоко-подходящие, а вот image нет.. Проблема в image в том, что с одной стороны вы пытаетесь сохранить структуру файла, а с другой предоставить ее как набор данных удобных пользователю.. Это две разные задачи и поэтому нежелательна реализация одной сущностью..

Автор: EvilsInterrupt 23.6.2013, 19:08
Цитата(mes @  23.6.2013,  19:51 Найти цитируемый пост)
 Такое можно проделывать со своим потоком, в котором Вы уверены..

Так у меня есть для этого операции первоначальных позиций для потоковых get/put указателей. Или Вы не об этом?

Добавлено через 9 минут и 22 секунды
Цитата(mes @  23.6.2013,  19:51 Найти цитируемый пост)
Это место требует обязательной разгрузки через сущность..

Наверное Вы про аналог sentry ?

Автор: mes 23.6.2013, 19:45
Цитата(EvilsInterrupt @  23.6.2013,  18:08 Найти цитируемый пост)
 Или Вы не об этом?

я о stream`e как о концепте последовательного очередного доступа.. 
работа с image предполагает (в приведенном примере) произвольный доступ. 

Автор: EvilsInterrupt 23.6.2013, 20:17
Цитата(mes @  23.6.2013,  20:45 Найти цитируемый пост)
я о stream`e как о концепте последовательного очередного доступа.. 

Я кажется понял о чем Вы. Потоки в идеале не имеют конца и операций произвольного перемещения указателя. То что применяется у меня в примерах это по сути Blob из мира Java. Однако хочу заметить, что далеко не все С++ программисты с этим согласны и приводят в пример std::fstream где вполне можно перемещать указатель куда хочешь с помощью seekg()/seekp()  ;)

Но я с Вашим примером не согласен и вот почему. Мне он кажется не объектно ориентированным. ООП мысль ставит во главу разработки "объекты", понимая под ними Абстрактные Типы Данных, т.е. набор данных и взаимосвязанных операций оперирующих с этим данными. Исходя из этого SFImage содержащий данные FileHeader, SecondHeaders , но не содержащий взаимосвязанных операций read()/write() не может являться объектом. Это просто некий storage, своего рода "коробка". В ООП на мой взгляд, если правильно понял Г.Буча объекты более интеллектуальны и именно поэтому read()/write() , которые Вы вынесли за пределы класса нужно вернуть обратно в пределы класса ;)

Автор: mes 23.6.2013, 20:47
начну с конца :

Цитата(EvilsInterrupt @  23.6.2013,  19:17 Найти цитируемый пост)
Вы вынесли за пределы класса нужно вернуть обратно в пределы класса ;) 

следует поправить : мы вынесли за пределы SFImage методы read(N_Header).. 
разницы нет по сути , находится ли read в классе или вне его.. Разница есть в каком классе он находится )
Я против когда read для N_Header, находится в приватной области SFImage smile

Цитата(EvilsInterrupt @  23.6.2013,  19:17 Найти цитируемый пост)
если правильно понял Г.Буча объекты более интеллектуальны и именно поэтому read()/write() 

не все объекты должны/могут быть интеллектуальны.. более того объект не должен выходить за рамки своих полномочий.. впрочем со своими обязанностями он действительно должен справляться хорошо.. Но вот опять же со "своими" smile

Цитата(EvilsInterrupt @  23.6.2013,  19:17 Найти цитируемый пост)
Исходя из этого SFImage содержащий данные FileHeader, SecondHeaders , но не содержащий взаимосвязанных операций read()/write() не может являться объектом. 

Ни в коем случае! ооп не определяется словом "содержит" ..Более того "содержит" уступает свое главенство "использует".. 

Цитата(EvilsInterrupt @  23.6.2013,  19:17 Найти цитируемый пост)
 понимая под ними Абстрактные Типы Данных, т.е. набор данных и взаимосвязанных операций оперирующих с этим данными

А вот тут правильно.. обратите на последнее : операции оперирующие этими данными.. Т.е. операции производятся над данными , над объектами и уж само определение говорит, что они (операции) могут выходить за рамки обьекта smile

Автор: mes 23.6.2013, 21:10
Цитата(EvilsInterrupt @  23.6.2013,  19:17 Найти цитируемый пост)
Однако хочу заметить, что далеко не все С++ программисты с этим согласны и приводят в пример std::fstream где вполне можно перемещать указатель куда хочешь с помощью seekg()/seekp()  ;)

во первых std далеко не идеал... исторически так сложилось..
во вторых fstream, это конкретная разновидность стрима, а частный случай, как известно не определяет общий smile 
в третьих я не против подобного неправомерного использования, если оно локально, и не навязывает свое поведение/логику пользователю..

Добавлено через 2 минуты и 3 секунды
Цитата(EvilsInterrupt @  23.6.2013,  19:17 Найти цитируемый пост)
Я кажется понял о чем Вы. Потоки в идеале не имеют конца и операций произвольного перемещения указателя

Да, если предоставляете поток пользователю, он должен быть похож на поток smile А если не похож, то значит это другая сущность smile

Автор: EvilsInterrupt 24.6.2013, 10:35
Цитата(mes @  23.6.2013,  19:51 Найти цитируемый пост)
т.е. сами header`ы у Вас вполне потоко-подходящие, а вот image нет.. Проблема в image в том, что с одной стороны вы пытаетесь сохранить структуру файла, а с другой предоставить ее как набор данных удобных пользователю.. Это две разные задачи и поэтому нежелательна реализация одной сущностью..

Прошу простить за назойливость, но не могли бы выразить эту мысль другими словами? Почему противоречивы задачи сохранения структуры данных файла и предоставления в удобном виде? 

Почти любой современный парсер чего-либо он в любом случае должен оставлять данные именно в том виде в каком подразумевает фомат этих данных, которые он парсил. Ну а стремление предоставить в удобном виде по-моему это стремление любого программиста, иначе зачем писать гуан-код?

Автор: mes 24.6.2013, 18:57
Цитата(EvilsInterrupt @  24.6.2013,  09:35 Найти цитируемый пост)
но не могли бы выразить эту мысль другими словами? 


определитесь, что есть для вас image ?
 структура данных, считанная с потока или 
 набор функций применяемых к структуре(файла) определенного формата ?

Добавлено через 1 минуту и 15 секунд
хм.. дежавю smile 

Автор: mes 2.7.2013, 19:43
EvilsInterrupt, продолжим поступенечно ?
можно ли условно свести формат к одному оглавлению и к набору данных для секций ?

Автор: EvilsInterrupt 3.7.2013, 09:08
mes, 
Прежде чем продолжим обсуждение про формат хотел бы знать точно что Вашу терминологию понимаю.

Является ли функция объявленная и определенная вне класса частью интерфейса класса?

Есть две ситуации, когда такую функцию пишет :
1) Автор класса
2) Программист-пользователь

Автор: Guinness 3.7.2013, 09:31
Цитата(EvilsInterrupt @  3.7.2013,  10:08 Найти цитируемый пост)
Является ли функция объявленная и определенная вне класса частью интерфейса класса?

В том же пространстве имен, что и класс или нет?

Автор: EvilsInterrupt 3.7.2013, 11:27
Guinness, В моем случае, мне кажется разницы никакой. Вопрос возник из-за прежних постов, где mes показал пример кода, в котором функция не объявленная в объекте работает с ним и при этом я понял что он считает такую функцию частью интерфейса объекта. Мне просто хочется уточнить и еще раз убедиться, что я ничего не напутал и что так оно и есть. До его примера я функции вне объекта считал функциями расширяющие интерфейс объекта, а не его частью.

Автор: Guinness 3.7.2013, 11:57
EvilsInterrupt, ну собственно так оно и есть. У Саттера была глава, где он заострял внимание на то, что такое интерфейс, попутно рассказав как работает поиск Кенига. И там есть зависимость от того, где Вы объявляете функцию, внутри какого пространства имен.
Вот в Си, например, нет методов(членов функций) для структур. Но мы ведь считаем все функции в хидере структуры её интерфейсом, если они принимают указатель на структуру.

Автор: EvilsInterrupt 3.7.2013, 12:04
Цитата(Guinness @  3.7.2013,  12:57 Найти цитируемый пост)
попутно рассказав как работает поиск Кенига.

Вы про Argument Depend Lookup ?

Цитата(Guinness @  3.7.2013,  12:57 Найти цитируемый пост)
И там есть зависимость от того, где Вы объявляете функцию, внутри какого пространства имен.

А как это влияет на интерфейс? Технически язык может позволять очень многое но это не означает что мы(программисты) должны разрабатывать как позволяет язык. Не знаю кто и где сказал, но есть разработка с помощью языка и есть как его величество язык позволит smile

То как работает ADL это-то понятно, но это же не значит что объект имеет метод если он с ним в одном и том же пространстве имен.

Добавлено через 12 минут и 14 секунд
Guinness, Спасибо за Саттера! Нашел то о чем ты говоришь. Это книга "Стандарты программирования на С++. 101 правило и рекомендации". Почитал сначала Правило №57 на стр.116 по началу был не согласен, но после Правила №44 стр. 93 понял, что выгодней считать свободные функции работающие с объектом, НО в пределах того пространства имен в котором объявлен объект.

Автор: Guinness 3.7.2013, 12:26
Цитата(EvilsInterrupt @  3.7.2013,  13:04 Найти цитируемый пост)
То как работает ADL это-то понятно, но это же не значит что объект имеет метод если он с ним в одном и том же пространстве имен.

Если в одном пространстве имён, то скорее всего он разрабатывался вместе с классом и является частью его интерфейса. Потому что разработчик данного класса разработал эту функцию для того, чтобы Вы её использовали при работе с классом, к которому она относится. Разве это не будет интерфейсом класса?
В объектных языках, таких как Java, такое невозможно , т.к. там нельзя создать функцию отдельно от класса. В Си, как я указал выше, это удинственная возможность ООП. Ну а С++ нечто среднее, и поэтому здесь интерфейс класса - это и то, что им является в Java, и то, что в Си.
Это мое мнение)

Автор: EvilsInterrupt 3.7.2013, 12:39
Цитата(Guinness @  3.7.2013,  13:26 Найти цитируемый пост)
В объектных языках, таких как Java, такое невозможно , т.к. там нельзя создать функцию отдельно от класса. 

Вот! Об этом же и Гради Буч говорит. Java и насколько понимаю и C# тоже думают о функции вне класса что и я, пока думаю, но после Саттера что-то вера в это колеблется smile
Думаю вопрос исчерпан, я достаточно гибок, как говорят: "Хоть горшком назови, только в печь не ставь". Мне никак не повредит если я буду считать интерфейсом то как его понимает Саттер, главное чтобы спустя время я осознал что так писать легче, а не труднее ;)

Остается уточнить у mes:

Цитата(mes @  2.7.2013,  20:43 Найти цитируемый пост)
можно ли условно свести формат к одному оглавлению и к набору данных для секций ? 

Что есть "оглавление" в Вашем понимании ?

Автор: mes 3.7.2013, 19:20
Цитата(EvilsInterrupt @  3.7.2013,  08:08 Найти цитируемый пост)
Является ли функция объявленная и определенная вне класса частью интерфейса класса?

немножко наоборот, интерфейс класса является частью , или частным случаем...

Добавлено через 1 минуту и 29 секунд
Цитата(EvilsInterrupt @  3.7.2013,  10:27 Найти цитируемый пост)
До его примера я функции вне объекта считал функциями расширяющие интерфейс объекта, а не его частью. 

А если функция мультиметод ?  smile

Добавлено через 5 минут и 47 секунд
Цитата(EvilsInterrupt @  3.7.2013,  11:04 Найти цитируемый пост)
Вы про Argument Depend Lookup ?

да это из той же оперы.. посмотрите например какую нибудь (бустовскую) библиотеку по сериализации, как они находят serialize и не жалуются снаружи она описанна или внутри ..

Добавлено через 8 минут и 43 секунды
и да уже несколько раз говорил и еще раз повторю.. Абсолютно не важно как описаны функции, важно какие операции предоставляет обьект.. И я возмутился не из за того, что функции были членами класса,
а из за того что были приватными членами, хотя именно эти операции и характеризуют взаимодействие с объектом(форматом)  smile

Добавлено через 9 минут и 47 секунд
Цитата(EvilsInterrupt @  3.7.2013,  11:39 Найти цитируемый пост)
Что есть "оглавление" в Вашем понимании ?

header

Добавлено через 14 минут и 21 секунду
Цитата(Guinness @  3.7.2013,  11:26 Найти цитируемый пост)
В объектных языках, таких как Java, такое невозможно , т.к. там нельзя создать функцию отдельно от класса. В Си, как я указал выше, это удинственная возможность ООП. Ну а С++ нечто среднее, и поэтому здесь интерфейс класса - это и то, что им является в Java, и то, что в Си.

Имхо, не С++ что то среднее, а в ява все пообрезали, чтоб своими несмышленными мыслями за пределы клеток не дай бог не упорхнули smile

Автор: mes 3.7.2013, 19:39
Цитата(EvilsInterrupt @  3.7.2013,  11:04 Найти цитируемый пост)
 НО в пределах того пространства имен в котором объявлен объект.

просто сила связи падает.. чем нужно большее сцепление, тем ближе к телу рассполагаем и наоборот smile
функция даже не работающая напрямую с объектом всего равно входит в тематическую группу интерфейса, если она логически с ним связанна..

Автор: EvilsInterrupt 7.7.2013, 10:53
При добавлении новой фичи в мою существующую утилиту, которая решает некоторые мои проблемы и моих знакомых я столкнулся с тем, что мне сложно ее добавлять. После размышлений пришел к выводу что "узким местом" являет код по работе с PE форматом. В виду того что это формат бинарного файла очень сложный, то мне бы хотелось послушать, а как другие программеры пишут код по работе с бинарными файлами? 

Вообщем суть темы обсудить\получить новые навыки по удобной работе с форматами бинарных файлов. Чтобы не усложнять я ввожу придуманный с головы формат, назову его SuperFormat, который обладает теми характеристиками, что встречаются в реальных форматах исполняемых файлах.

Опишу и дополню первоначальную задачу поста.

Цель: Разработка класса\библиотеки по работе с форматом файла. Нужно читать\писать из файла этого формата. Условно назовем формат файла SuperFormat.

Некоторые нюансы формата:
1) Существует разные вариации для 32 и 64 бита.
2) Все заголовки(структуры) используемые в формате имеют одинаковый набор структур и названий полей в структурах для обоих режимов. Отличия в том, что в некоторых типов полей uint32_t или uint64_t зависит от битности файла.
3) Расположение некоторых заголовков в файле можно узнать только прочитав некоторые другие заголовки

Формат описывается следующими структурами:

Код

#include <PshPack1.h>

struct FirstHeader_t
{
   uint16_t  signature;
   uint32_t  secondHeaderOffset;
};

struct SecondHeader32_t
{
   uint32_t signature;
   uint32_t specific_field;
};

struct SecondHeader64_t
{
   uint32_t signature;
   uint64_t specific_field;
};

#include <PopPack.h>


Требования к коду:
Писать обобщенно независимо от битности формата. У меня пока это достигнуто с помощью C++ идиомы traits.

P.S.:
Подобные вещи встречаются на практике. К примеру если посмотреть в WinNt.h хидер идущий с MSVS то можно увидеть структуры IMAGE_NT_HEADERS32 и IMAGE_NT_HEADERS64. Эти структуры имеют один и тот же набор полей и имен этих полей, но некоторые поля отличаются типом. Эти структуры описывают PE32\PE32+ форматы. Но точно такая же ситуация и с Elf, Mach-O.

Автор: mes 7.7.2013, 17:26
EvilsInterrupt, вот опять ставите мелочи во главу угла... У вас есть уже готовый (распланированный) и удобный концепт для варианта с одной архитектурой ?
если нет, то не думаю, что стоит на этом заморачиваться, хотя бы по проблеме того, что не понятно на каком уровне нужно производить селекцию подходящего типа.. 


Цитата(EvilsInterrupt @  7.7.2013,  09:53 Найти цитируемый пост)
Писать обобщенно независимо от битности формата. У меня пока это достигнуто с помощью C++ идиомы traits.

как минимум если используете трайтс, то логически его использовальзовать для всех структур-хэдеров, а не только тех, которые нравятсая.. 

т.е. 
Код

template<T>
struct SF
{
   header1_t header1;
   select<T>::header2 header2;
}

выглядит необоснованно однобоко, в отличии  от 
Код

template<T>
struct SF
{
   select<T>::header1 header1;
   select<T>::header2 header2;
}

кстати концепт traits, подразумевает передачу этого traits, как шаблонный параметр.. вроде у Вас по другому... это не имеет значения, кроме как путаницы.. 

и так сбоку помимо traits есть еще if_-селекция.. 

Автор: EvilsInterrupt 7.7.2013, 18:36
Цитата(mes @  7.7.2013,  18:26 Найти цитируемый пост)
выглядит необоснованно однобоко, в отличии  от 

Ну если присмотреть к объявлению FirstHeader_t, то видно что от битности не зависит! Смысл его загонять в traits-типы?

Цитата(mes @  7.7.2013,  18:26 Найти цитируемый пост)
а не только тех, которые нравятсая.. 

Не те что нравятся, а те что нуждаются в этом обобщении!
Как я уже привел выше FirstHeader_t в этом обобщении не нуждается. Зато типы SecondHeader32_t и SecondHeader64_t к общему SecondHeader_t нуждаются!

P.S.:
Цитата(mes @  7.7.2013,  18:26 Найти цитируемый пост)
вот опять ставите мелочи во главу угла...

Ну как вся наша жизнь состоит из мелочей! ;) Куда уж без них?

Автор: EvilsInterrupt 7.7.2013, 20:35
Цитата(mes @  7.7.2013,  18:26 Найти цитируемый пост)
и так сбоку помимо traits есть еще if_-селекция.. 

ВЫ об идиоме упомянули http://en.wikibooks.org/w/index.php?title=More_C%2B%2B_Idioms/Type_Selection&printable=yes ?

Автор: mes 7.7.2013, 23:17
Цитата(EvilsInterrupt @  7.7.2013,  19:35 Найти цитируемый пост)
ВЫ об идиоме упомянули type selection ? 

да об ней smile

Добавлено через 5 минут и 18 секунд
Цитата(EvilsInterrupt @  7.7.2013,  17:36 Найти цитируемый пост)
Ну как вся наша жизнь состоит из мелочей! ;) Куда уж без них?

мелочи хороши когда имеете общее представление, а так не имея хорошего концепта уделять вниманию, каким способом выбирать тип для описания - имхо излишне.. 

Цитата(EvilsInterrupt @  7.7.2013,  17:36 Найти цитируемый пост)
Как я уже привел выше FirstHeader_t в этом обобщении не нуждается. Зато типы SecondHeader32_t и SecondHeader64_t к общему SecondHeader_t нуждаются!

это с вашей точки зрения, а не с точки зрения Traits.. Так к примеру если придется добавить третью архитектуру, то вполне возможно там будет отличаться.. именно в этом идея трайтс... 
а так нагородили лишних сущностей без сильной надобности.. 

Автор: mes 7.7.2013, 23:35
вот пример на скорую руку :
http://codepad.org/fJ5HPr0u

Автор: SenkraD 8.7.2013, 10:30
Цитата(mes @  7.7.2013,  23:35 Найти цитируемый пост)
вот пример на скорую руку :
http://codepad.org/fJ5HPr0u
а не могли б код сюда выложить? унекоторых на работе не все сайты открыты 

Автор: EvilsInterrupt 8.7.2013, 11:29
Цитата(SenkraD @  8.7.2013,  11:30 Найти цитируемый пост)
а не могли б код сюда выложить? унекоторых на работе не все сайты открыты 

Не думаю, что mes обидится если выполню просьбу вместо него. Вот:
Код

#include <iostream>

template<bool, typename TypeTrue, typename TypeFalse>
struct if_
{
  typedef TypeTrue type;
};

template<typename TypeTrue, typename TypeFalse>
struct if_<false, TypeTrue, TypeFalse>
{
  typedef TypeFalse type;
};

namespace variants {
  enum num { a32, a64 };
}
typedef variants::num variant;



template<variant V>
struct test
{
  typedef
    typename if_<V==variants::a32, double,
    typename if_<V==variants::a64, int, void>::type>::type type;
};


int main()
{
 std::cout 
   << test<variants::a32>::type(5.6) << " :: "
   << test<variants::a64>::type(5.6) << std::endl;
}

Автор: mes 8.7.2013, 13:44
Цитата(EvilsInterrupt @  8.7.2013,  10:29 Найти цитируемый пост)
не думаю, что mes обидится если выполню просьбу вместо него. 

 smile  smile 

в итоге селекция типа должна разрешиться через обозначенный формат, например 

Код

typedef format<a32> format32;
format32::header_type header;

Автор: EvilsInterrupt 9.7.2013, 12:32
Цитата(mes @  7.7.2013,  18:26 Найти цитируемый пост)
У вас есть уже готовый (распланированный) и удобный концепт для варианта с одной архитектурой ?

Я правильно понял, что применяя термин "удобный концепт" Вы вкладываете "интерфейс будущей библиотеки"?

Автор: mes 9.7.2013, 18:07
не совсем.. концепт это та идея, что связывает интерфейс с имплементацией.. например хотим разработать класс фигура.. для начала нам нужно определитъся, что все таки от фигуры требуется и что она будет предоставлять.. 
будем ли мы ее рисовать, изменять, совмещать, сохранять ? и кто она является по сути : строчка, число, свойство, структура даныных, объект ? когда мы знаем, что мы строим, можем думая об интерфейсе приступатъ к реализации..

Добавлено через 5 минут и 39 секунд
к примеру для обсуждаемого формата  (как библиотечной сущности) я предполагаю чтение произвольной записи, и построение изображения на основе считанного, и возможно комбинирование данных...

Добавлено через 6 минут и 18 секунд
Цитата(mes @  9.7.2013,  17:07 Найти цитируемый пост)
изображения
image

Автор: mes 9.7.2013, 22:14
Цитата(mes @  9.7.2013,  17:07 Найти цитируемый пост)
 я предполагаю

т.е. я исхожу из предположения, что перечисленное далее должно быть либо реализованно, либо иметь возможность быть реализованным.. но с моей колокольни эти и другие назначения как за туманом и трудно определить их обоснованность..

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)