Tag Archives: mingw32

ChaiScript и mingw32

ChaiScript — сравнительно простой C++-подобный скриптовый язык, предназначенный для встраивания в C++-приложения. Недавно я попробовал его задействовать с одной из сборок mingw32 (gcc-6.3.0-64).

Итак, берём исходник helloworld с головной страницы сайта ChaiScript:

#include <chaiscript/chaiscript.hpp>

std::string helloWorld(const std::string &t_name) {
  return "Hello " + t_name + "!";
}

int main() {
  chaiscript::ChaiScript chai;
  chai.add(chaiscript::fun(&helloWorld), "helloWorld");

  chai.eval(R"(
    puts(helloWorld("Bob"));
  )");
}

и вперёд, с песней… а откуда столько ошибок компиляции?

error: 'mutex' in namespace 'std' does not name a type

что, правда что ли? А как же полная поддержка C++14 в gcc 6.3 и всё такое? Открываем стандартный заголовочный файл mutex и видим

#ifdef _GLIBCXX_HAS_GTHREADS

  // Common base class for std::recursive_mutex and std::recursive_timed_mutex
  class __recursive_mutex_base
  {
...

Легко убедиться, что макрос _GLIBCXX_HAS_GTHREADS, оказывается, не определён.
Что делать? Ответы со StackOverflow:

  1. на Windows использовать MSVC (ага, уже);
  2. использовать Boost.Thread или ещё какую-нибудь стороннюю библиотеку (ну да, и перелицовывать на неё весь чужой код);
  3. найти другую сборку mingw32 с каким-то образом прикрученными C++threads, например, TDM GCC (да, только вот TDM GCC застрял на версии компилятора 5.1… вообще странно — OpenMP работает, а C++threads не шмогла?..);
  4. сделать свою сборку mingw32 с каким-то образом прикрученными C++threads (спасибо, может быть когда-нибудь потом…);
  5. использовать drop-in замену на основе Win32, например, mingw-std-threads (о, это может сработать!).

Итак, копирую файлы mingw.thread.h, mingw.mutex.h, mingw.condition_variable.h. Добавляю инклюды перед чайскриптом:

#include <mingw.thread.h>
#include <mingw.mutex.h>
#include <mingw.condition_variable.h>
#include <chaiscript/chaiscript.hpp>

Компилируем… Опять куча ошибок, например,

mingw.condition_variable.h 192 error: 'unique_lock' has not been declared

Ну ладно, это всё тот же mutex, добавим и его инклюд после mingw.mutex.h. И полюбуемся на новую кучу ошибок, например:

std_mutex.h 132 error: redefinition of 'struct std::defer_lock_t'

Ок, поменяем местами инклюды мьютексов. Компилирум… И наблюдаем ошибки снова в коде ChaiScript, например:

chaiscript_stdlib.hpp 57 error: invalid use of incomplete type 'class std::future'

Srsly? Incomplete type? Хммм… А где вообще этот future определён? Во future? Лезем туда и любуемся на старый добрый _GLIBCXX_HAS_GTHREADS, закрывающий основную часть кода (включая async, кстати). А не обмануть ли тебя? Попробуем.

#ifndef _GLIBCXX_HAS_GTHREADS
#include <mingw.thread.h>
#include <mutex>
#include <mingw.mutex.h>
#include <mingw.condition_variable.h>

#define _GLIBCXX_HAS_GTHREADS
#include <future>
#undef _GLIBCXX_HAS_GTHREADS
#endif // _GLIBCXX_HAS_GTHREADS

И получаем опять переопределения.

thread 61 error: redefinition of 'class std::thread'

Добавим ещё инклюдов

#ifndef _GLIBCXX_HAS_GTHREADS
#include <thread>
#include <mingw.thread.h>
#include <mutex>
#include <mingw.mutex.h>
#include <condition_variable>
#include <mingw.condition_variable.h>

#define _GLIBCXX_HAS_GTHREADS
#include <future>
#undef _GLIBCXX_HAS_GTHREADS
#endif // _GLIBCXX_HAS_GTHREADS

Число ошибок уменьшилось… Оказывается, нет стандартных определений once_flag и call_once:

future 312 error: 'once_flag' does not name a type

Т.е. в mingw-std-threads кое-чего не хватает. Стандартные определения (в mutex) использовать нельзя, так как они опираются на отсутствующую библиотеку pthreads (кстати, на всякий случай упомяну, что попытки её подключить через параметры компилятора не увенчались успехом).

Естественная мысль: а может сделать это уже руками?.. Я попробовал. Результат — одна ошибка

future 548 error: expected class-name before '{' token

фактически там встретился неопределённый идентификатор __at_thread_exit_elt (имя класса)… В общем, реализовывать ещё и замены подобных потрохов мне стало как-то резко влом.

В принципе, ChaiScript позволяет попросту отключить C++threads (и внутреннюю синхронизацию) через макрос CHAISCRIPT_NO_THREADS. Итак, выкинем всю требуху:

#define CHAISCRIPT_NO_THREADS
#include <chaiscript/chaiscript.hpp>

Компилируем… Долго… И фейл!

Fatal error: can't close obj\Debug\main.o: File too big

WTF?! StackOverflow подсказывает, что проблема в ограничениях формата экзешников PE/COFF (от «coffin», что ли?). К счастью, в этом конкретном случае она решается включением оптимизации (хотя бы -Og или -O2), благодаря чему размер объектов получается меньше. Собираем. Три минуты (helloworld? да, здесь у меня не шибко быстрый процессор, но всё же…)? Exe в 40Мб? Круто, чё. Но оно наконец-то работает — сообщение выводится.

Справедливости ради: на -O3 сборка занимает около одной минуты и exe имеет размер 2.7Мб.

Для «надёжности» можно добавить ещё один параметр компилятора: -Wa,-mbig-obj (именно так, через запятую без пробелов). Но как-то всё это не воодушевляет, да.