Pull to refresh
0
EXANTE
Инвестиционная компания нового поколения

Борьба бобра с ослом, или Адаптация MSVC кода под gcc

Reading time3 min
Views8.9K

Статья описывает некоторые затруднения, которы мы встретили при попытке адаптации одного из наших старых Windows-only проектов (плагин к MT4 серверу) к кросскомпиляции под Linux (CI, статический анализ, автотесты и прочие модные слова). Точнее, в коде присутствовал ряд конструкций, которые спокойно съедались MSVC, но категорически отказывались компилироваться с использованием mingw/gcc.


image


Под катом 7 наиболее часто встретившихся примеров кода, которые будут компилироваться MSVC, но не будут с gcc, и способы это лечить.


Дисклеймер


Цель статьи – не сказать, что какой-то компилятор лучше, чем другие, а указать на некоторые проблемы, которые могут возникнуть при адаптации кода к другим компиляторам (особенно если до этого использовался только MSVC). Также некоторые (если не все) элементы поведения можно свести к одному, если подкрутить флаги компиляции, но ведь лучше все-таки поправить код (хотя бы и sed'ом), правда?


Если вы начинающий трейер, то предлагаем вам почитать тут.


Условия задачи


Имеем среднего размера проект (около 15к SLOC не считая библиотек), в котором используется CMake с практически дефолтными флагами компиляции. MSVC используем 14 версии, а mingw-gcc — 6.3.


Найденные проблемы


Декорация имен методов


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


__declspec(dllexport) void SomeMethod() {}

При компиляции gcc имя функции декорировалось, что приводило к тому, что сервер не определял метод в плагине. Более правильное (рабочее) решение:


extern "C" __declspec(dllexport) void SomeMethod() {}

Пути к файлам и include


Различие разделителей в путях на разных системах также приводит к ошибкам на этапе компиляции. Код


#include "directory\\include.h"

откажется компилироваться под Linux/gcc, хотя под Windows/MSVC никаких проблем не будет. Это не совсем ошибка, но следует отметить, что для удобства переносимости лучше все же использовать обычный слэш, поскольку он воспринимается большинством систем. С путями также есть и другая проблема...


Регистр и include


Как вы, вероятно, знаете, пути some/path и SoMe/pATh в Windows не различаются, но это не так в некоторых других системах, что приводило к ошибкам, если программист указывал путь в заголовочному файлу без учета регистра. Например:


#include <Winsock2.h>

выдаст ошибку с gcc под Linux, потому что указанный файл просто не будет найден. Аналогичная проблема также наблюдается с именами библиотек, например, Ws2_32 против ws2_32.


Как определить целевую платформу


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


#ifndef _MSC_VER
#include <unistd.h>
#endif

Не надо так делать. При использовании mingw _MSC_VER не определяется, вместо этого правильнее проверять _WIN32 для определения целевой платформы, а _MSC_VER использовать, только если вы хотите включить код, специфичный для MSVC.


Pure virtual методы


Код


class SomeClass
{
  virtual void someMethod() = NULL;
};

при попытке компиляции gcc радостно скажет


invalid pure specifier (only «= 0» is allowed)

но не вызовет ошибок у MSVC. Причина проста: gcc раскрывает макрос NULL не в 0, а в __null (что, в общем-то, совсем не запрещено). Решение: очевидно, отказаться от использования NULL для указания pure virtual методов и использовать = 0.


Определение методов внутри заголовочного файла


Код


class SomeClass
{
  SomeClass::SomeClass() {};
};

при использовании gcc выдаст


extra qualification ‘SomeClass::’ on member ‘SomeClass’

Правильный ответ, очевидно, не должен содержать SomeClass::. Вообще, в драфте стандарта C++14 (параграф 8.3) написано, что:


the declaration shall refer to a previously declared member of the class or namespace to which the qualifier refers

Декларация переменной без указания переменной


Код, написанный с использованием клипбордного интерфейса


void someMethod()
{
  SomeClass;
  SomeClass class;
}

содержит в себе ошибку, которая игнорируется MSVC, но вызовет ошибку на этапе компиляции у gcc:


declaration does not declare anything

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


Вместо послесловия


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

Tags:
Hubs:
+15
Comments12

Articles

Information

Website
exante.eu
Registered
Founded
Employees
101–200 employees
Location
Мальта