Ответы пользователя по тегу C++
  • Как обойтись без библиотеки импорта?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Библиотека импорта для dll - это вспомогательный артефакт связывания основного образа программы с динамическим модулем.
    В общем - никак, раз уж явную загрузку тебе не предлагать. :)

    Иное дело - это твои хитромудреные манипуляции для установления зависимости между основным и динамическими модулями. Зачем тебе потребовалось явно указывать линковку с lib файлом?
    У каждого проекта студии есть список зависимостей (References). Если в проекте главного модуля программы установить зависимость от проекта динамической библиотеки, то msbuild все свяжет за тебя.

    А еще есть нестандартная но широко поддерживаемая #pragma comment(lib,"xxx.lib").
    Ответ написан
    Комментировать
  • Учитывается ли тип значения, возвращаемого функцией, в решении о выборе нужной версии перегруженной функции?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Стандартом продиктовано что функции могут перегружаться только параметрами.
    eel.is/c++draft/over#2

    В то же время, стандартом описано что функция не может быть перегружена только лишь изменением возвращаемого значения.
    eel.is/c++draft/over.load#2

    А вот пояснения зарыты глубоко в 13м параграфе стандарта и отрыть их там наскоро не выйдет.
    eel.is/c++draft/#over
    И дело там не только в неявных приведениях типа. Проблем с разрешением перегрузки или инстанциирования шаблона только по возвращаемому значению выше крыши.
    Ответ написан
    Комментировать
  • Какие задачи на C / C++ сейчас востребованы?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Понимаешь, практика показывает, что востребованным может стать абсолютно любая библиотека. Особенно если ты берешь на себя ответственность ее поддержки.
    Людей много, задачи они порождают все время и самые разные. Зачастую, количество потребностей в коде у человека превышает возможности его создания и поддержки. И фактор нехватки ресурсов (времени/рук) чаще всего играет решающую роль в отказе от идеи.

    К тому же, у художников вот есть такие места, как deviantart.com / cghub.com (был), где они выкладывают свои работы, по которым видно их рост и общий стаж в ремесле. Я считаю что и у разработчика тоже можно проследить тенденцию роста по его проектам на гитхабе, а по открытым доскам на trello.com можно еще и проследить уровень самоорганизации человека.

    Нашему IT рынку сейчас более нужны не гениальные раздолбаи, а собранные и самоорганизованные специалисты. Поэтому мой совет вот такой: лучше нацелиться на простые и организованные проекты из личного интереса, чем искать что либо выдающееся.
    Ответ написан
    Комментировать
  • Какой проект начать разрабатывать, чтобы продемонстрировать свои знания "работадателю"?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    У разработчиков часто руки чешутся что-нибудь свое накатать. И не всегда это прямо какие-то практические проекты. Чаще это просто примеры работы какого-либо подхода, какие-либо тесты, какие-нибудь наброски. И часто это все сваливается в личном пространстве гитхаба.

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

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

    Советую выбрать проект именно из своих желаний. Из того, за что руки чешутся взяться.
    А если хочется не для себя, а для собеседований, то лично я советую сразу начать стыдиться такого проекта и никому его не показывать.
    И еще советую прочитать вот это: blog.gamedeff.com/?p=64 Актуальность текста лежит далеко за пределами геймдева.
    Ответ написан
    Комментировать
  • Как решить проблему с ООП?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Да вы, батенька, король контента!

    Эт шутка. Хоть и не смешная для самоделкиных из области геймдева. :)
    Так, а кроме шуток. Не надо вообще так делать. Тип наносимого урона/снаряда/юнита/орудия никогда не должен описываться его собственным классом. Все юниты должны быть одного класса, все пули - тоже, все здания - тоже.
    У тебя должен быть только набор общих классов для сущностей. Конкретные параметры (что откуда вылетает, как дамажит и как это все визуально выглядит) должны лежать в ресурсах.
    Рекомендую почитать: habrahabr.ru/post/255561 Изложенное куда более важно для области любимых самокатов на С++.

    Хотя... Если тебе так надо реализовать на классах, а не на ресурсах, то тебе надо просто организовать фактическую связь между сущностями. Давай предложу один страшный вариант... Дебажить это все чур самому.
    class TowerParent
    {
    public:
    	// Это тип для той штуки, которая будет создавать нам нужную боеголовку.
    	typedef FireParent* (*FireConstructor)( float, float, int, float, float, float );
    	
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    	
    	FireParent* towerFire;
    protected:
    	FireConstructor	fire_constructor; // инстанция конструктора боеголовок.
    	
    	// Вот так мы будем определять тип конструируемой боеголовки.
    	template< class fire_t >
    	inline void DefineFireType(){
    		fire_constructor = TowerParent::template MakeFire<fire_t>;
    	};
    	
    private:
    	// А вот так мы будем конструировать боеголовку.
    	template< class fire_t >
    	static FireParent* MakeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    
    SimpleTower( int _col, int _row, float _posX, float _posY, float _attackRange, float _angle ){
    	DefineFireType<Bullet>();
    };
    
    RocketTower(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle){
    	DefineFireType<Rocket>();
    };


    Конструирование делается через:
    towerFire = fire_constructor( .... );

    Еще одна поправка. Не Rocket, а Missile. Потому что ... :)

    UPD:
    Еще один вариант, уже менее страшный - это шаблонизация базового класса:
    template< class fire_t >
    class TowerParent
    {
    public:
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    
    	fire_t* towerFire;
    
    protected:
    	inline fire_t* MkeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    class Bullet : public FireParent<Bullet>
    {
    public:
    	Bullet(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    
    class Rocket : public FireParent<Rocket>
    {
    public:
    	Rocket(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    Ответ написан
    1 комментарий
  • C++ teplate class. Пoмогите! Почему вылетает segfault при попытке запихнуть std::string?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Штука первая, делать надо не так:
    array = (Type*) malloc(100);
    а так:
    array = new Type[ 100 ];
    или, лучше, вот так:
    array = static_cast<Type*>( new uint8_t[ 100 * sizeof( Type ) ] );


    Штука вторая. У тебя в выделенном блоке памяти мусор лежит. Надо сперва память подготовить. Делаем вот так:
    memset( array, 0, 100 * sizeof( Type ) );
    Это приведет к очистке памяти. Но и после этого память все еще нельзя использовать.

    Следом надо переделать вот так:
    void push_back(Type elem) {
        new( array ) Type();
        array[0] = elem;
      }

    или так:
    void push_back(Type elem) {
        new( array ) Type( elem );
      }
    Ответ написан
    Комментировать
  • Что должна вернуть функция NULL, если ничего не найдено?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Так... давай-ка немного поправим твое понимание предмета.

    В С/С++ существует такой тип даных, как указатель на данные. Его синтаксис выглядит так:
    int* value;

    Запись буквально означает что 'value' у нас будет хранить адрес на область данных с предположительной типизацией в четырехбайтовое знаковое целое. Внимательно! 'value' хранит только указатель, никаких данных о размере блока этих данных нет, никаких строгих оговорок о типе этих данных нет (только предположение что это 'int').

    При формировании переменной оная, обычно, ничем никак не инициализируется. То есть, после определения нашего 'value' в его значении лежит любой немыслимый мусор. Внимательно! Нет никаких способов (кроме как обратиться и словить AV/SIGSEGV) определить что значение 'value' ссылается на правильный адрес. Поэтому этот самый мусор, который в 'value' и содержится, можно спокойно использовать как адрес блока данных и при обращении по этому адресу получить от ОСи по рукам.

    Вопрос! Как этого избежать?
    Есть очень простой и очень старый выход - определить некую магическую константу, которая точно смогла бы символизировать чистоту указателя (что указатель не одержит адреса). Именно такой константой 'NULL' и является.
    Адрес на блок данных может быть абсолютно любым! Он может быть даже 0xA0L. Но если в значении указателя записан 0 (это и есть NULL), значит указатель чист - он не содержит в себе адрес на блок данных.

    Итак! NULL - это не зло. NULL - это признак чистоты указателя!

    Теперь перейдем к "тонкостям и нюансам нолика"...

    Попробуй собрать такой код:
    typedef int* p_int;
    
    p_int value = p_int();
    printf( "ptr : 0x%08x\n", value );

    Вывод в консои будет : "ptr : 0x00000000". О чем это говорит? Это говорит о том, что возвращаемое инициализатором указателя значение (вот эта вот запись: "p_int()") всегда эквивалентно NULL.

    Можно было бы с точно таким же холоднокровием написать вот так:
    p_int value = NULL;
    И все осталось бы по прежнему.

    Дело в том, что NULL имеет тип "void*", а этот тип можно преобразовать абсолютно к любому иному указателю.
    И NULL самостоятельно приводится к нужному типу в операторе присвоения указателя этого типа. Ноль - он и в Африке ноль.

    Попробуй собрать такой код:
    int* value = NULL;
    delete value;


    Если бы 'value' небыл инициализирован, то с превеликой вероятностью оператор delete привел бы к падению приложения. 'delete' воспринял бы мусор как правильный указатель и попробовал бы освободить память по этому указателю, а так как это мусор, ОСь эту попытку забрила бы, выдав программе красную карточку. Вместе с тем, оператор delete спроектирован так, чтобы не обращать внимания на NULL. Оператор просто тихо завершается если видит нулевой адрес.
    Вывод: чистота NULL не нуждается в большей очистке! :)

    Вот так. Надеюсь, моя простыня текста хоть немного да поможет тебе.

    А по вопросу о функции - да возвращай NULL! Так все делают, чем ты хуже? ;)
    Ответ написан
    Комментировать
  • Какие ЯП не требуют кучу прикладнухи для устройства на работу?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    LUA скриптер может знать только LUA и работать без затруднений.
    Python скриптер может знать только свой ЯП и спокойно работать.

    Уяснить тебе стоит одну очень важную вещь. Один лишь нужный ЯП знать для работы может только скриптер.
    Для разработчика-же крайне важны знания как можно более широкой информационной периферии своей области.
    Ищи игровые студии/компании которым нужны LUA/Python скриптеры. Но запомнить надо еще одну вещь - для очень многих людей это дорога без возврата.
    Ответ написан
    1 комментарий
  • Предподчтительные средства разработки под C++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Не хотелось бы показаться экстремистом, но все же...
    В бизнесе нет места хобби, это даже я (фанатик и наркоман кодописания, получающий кайф просто от написания кода) был вынужден осознать.
    В бизнесе важнее всего малое время разработки с соблюдением как можно более высокого качества конечного продукта. Если у тебя получится показать приемлемую скорость выполнения задач с использованием консольных редакторов текста, отладчика и средств менеджмента кода, то ни кто и слова не скажет.
    Однако, практика показывает что в бизнесе удобнее использовать комплексные решения сборки проектов. От сред разработки и до средств непрерывной интеграции.
    Ответ написан
  • Можно ли перегружать базовые типы в С++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Немного расширю ответ MiiNiPaa.

    Добавить поддержку шаблонных компараторов можно таким образом:
    template< typename item_t, template< typename > class item_traits >
    class item_processor{
    private:
    	typedef item_traits<item_t> traits_t;
    	...
    };


    Изюмина во втором параметре шаблона - это шаблонный параметр шаблона. :)
    У Девида Вандервуда по этому поводу было даже так написано: "Поскольку параметр шаблонного параметра шаблона не используется, его имя можно опустить".
    Такая запись говорит о том, что при инстанцировании или специализации шаблона в его объявлении ожидается только имя шаблона, а не его инстанцирование.

    Используется это дело вот так:
    template< typename target_t >
    class traits_less;
    
    ...
    
    item_processor<char, traits_less> char_processor;


    Все компараторы STL имеют один параметр шаблона. Таким образом, их использование тоже становится допустимо в качестве параметра для item_processor. В то же время, и свой компаратор в такую систему добавить проблемы не составит.
    Ответ написан
    Комментировать
  • Как трактовать определение в двух разных модулях классов с одинаковым именем?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Именно такую ситуацию имел в команде пару месяцев назад. Человек использовал небольшой вспомогательный класс внутри реализации (.cpp) юнит-тестов. И оба раза назвал этот впосогательный класс одинаково. Код в этом случае прекрасно компилируется, только работает как граната.

    С точки зрения стандарта ситуация трактуется как штатная. Компиляция производится независимо для каждого файла, объявления + реалиции этих самых 'struct A' укладываются по своим объектным файлам, а потом ликовщик увязывает этот код как получится. В результате создаваемые экземпляры не всегда могут соответствовать локальному описанию.

    Обычный линковщик ожидает что на вход к нему будут поданы уже готовые к линейной линковке блоки. Обычный линковщик, если его не попросить, связывает только уже используемые участки кода, начиная с точки входа или точек экспорта. Сортировка блоков (библиотек и модулей) обычно топологическая, но с сохранением алфавитного порядка между модулями в рамках одного ранга. Вот и получается, что "ликовщик увязывает этот код как получится".
    Линковщик в очередном модуле встречает еще не связанное, но уже используемое где-то имя типа и генерирует для этого типа код. Далее, в другом модуле линковщик снова встречает имя этого же уже связанного типа и просто отбрасывает его. Но отбрасывается не весь тип, а только уже связанные его части. Если во втором рассматриваемом типе будет находиться иной набор функций, они будут подвязаны к набору функций уже встроенного типа. И вот с этого места начинается дорога в ад, т.к. у двух таких типов может быть разный размер, разные поля в состоянии, разное выравнивание, разная реализация одинаково названных функций.

    Выход из ситуации:
    • использовать Forward declaration для таких локальных классов (добиться ошибки 'class redefinition' в таких случаях);
    • обертывать описания в локальные неименованные namespace (добиться уникальности пространства для таких классов);
    • описывать такие локальные классы как nested-классы от глобальных (делать хотяб Forward declaration в пространстве глобального класса - тоже упор на уникальность пространства);
    Ответ написан
  • Как загрузить DDS файл в c++ builder?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    DDS - Direct Draw Surface - это формат контейнера, заполненного одним или несколькими изображениями, снабженного специальным DDS заголовком.
    Как правило, в таком формате хранятся сжатые аппаратно-декодируемыми методами текстуры (S3TC, PVR, ATC, ETC). Вместе с тем, контейнер позволяет хранить и несжатые цветовые данные.
    Тип метода сжатия указан в DDS-заголовке, в полле "_fourcc", в виде четырехсимвольной строки.

    Пример описания DDS-заголовка:
    https://github.com/FrankStain/tex-conv/blob/master...

    Пример непосредственной загрузки DDS файла:
    https://github.com/FrankStain/tex-conv/blob/master...

    При загрузке DDS-файла, первым этапом читаем заголовок, все что дальше заголовка - это изображени(е|я).
    Если в заголовке файла указан формат сжатия данных, то перед их ручным использованием данные надо распаковать. Несжатые данные (RGB(1|4|8)(A) форматы) можно использовать прямо из файла.
    Писать распаковщик формата сжатия своими руками - дело затруднительное. Легче взять готовые библиотеки у непосредственных разработчиков или посредников этих форматов.
    ATC распаковывается с помощью Adreno SDK ( https://developer.qualcomm.com/download ).
    PVR распаковывается с помощью PowerVR SDK ( community.imgtec.com/developers/powervr/graphics-sdk ).
    DXT или S3TC обрабатывают как оба вышеперечисленных пакета, так и DirectX SDK. Так же существует набор утилит от nVidia - Texture Tools ( https://developer.nvidia.com/legacy-texture-tools ).
    Ответ написан
    Комментировать