Страницы

Поиск по вопросам

Показаны сообщения с ярлыком boost. Показать все сообщения
Показаны сообщения с ярлыком boost. Показать все сообщения

понедельник, 13 апреля 2020 г.

boost::regex и pcre исключение Ran out of stack space trying to match the regular expression

#cpp #регулярные_выражения #boost #pcre

                    
Когда то давно задавал подобный вопрос на другом форуме, но никто внятно не ответил.
Есть две библиотеки для работы с регулярными выражениями по стандарту PCRE.
Это boost::regex (или урезанный вариант std::regex) и библиотека PCRE.
Собственно есть сложное регулярное выражение.
там присутствуют подобные выражения
"+string_open+"(?>(?>(?!"+string_open+"|"+string_close+").)+|(?R))*"+string_close+"

Всё это дело замечательно отрабатывает для небольших входящих строк. но когда входящая
строка превышает допустим 1000 символов и условия указанные в регулярном выражении
выполняются, то выбрасывается исключение и в boost и в pcre.
Ran out of stack space trying to match the regular expression.
Собственно здесь говорится что происходит выход за пределы некоего стека.
1) Библиотеку pcre можно собрать из исходников, и увеличить там некоторые константы
#define , точно не помню что именно там было, то ли указание на использование рекурсивной
модели работы то ли еще что то.
Но в общем скомпилированная таким образом библиотека pcre стала работать с неограниченными
по объему строками и решала поставленную задачу. Сейчас используется именно такая модель.
Однако данный метод увеличил время работы регулярных выражений в 10-20 раз. 
раньше программа поднималась за 4 секунды, а с использованием такого подхода 40-50
секунд.
2) В boost ни где не смог найти информацию, о том как и где изменять значения этого
стека. 
Что из себя представляет этот стек? Этот тип формируют сами библиотеки?
Невозможность изменить этот стек, выглядит нелогичным, потому что 
программа нормально отрабатывается для строки допустим 990 символов.
Время выполнения разбора 5 миллисекунд. Занимаемая память 400 кб.
Добавляются 20 символов и всё ran out stack.    


Ответы

Ответ 1



Это читали? Что бы сделал я? Попробовал обе опции: и BOOST_REGEX_RECURSIVE, и BOOST_REGEX_NON_RECURSIVE; Увеличил бы BOOST_REGEX_BLOCKSIZE и BOOST_REGEX_MAX_BLOCKS.

Ответ 2



Для такой задачи лучше использовать более серьёзный парсер, нежели регулярные выражения. Например, boost::spirit. Обе проблемы — стека и быстродействия — решаются сами собой. Из "недостатков" могу отметить большее время компиляции (порядка десятков секунд, частично решаемо с помошью прекомпилированных хэдеров) и достаточно крутая в начале кривая обучения (там активно используются ленивые вычисления, к логике которых надо привыкнуть — но дальше всё проще).

суббота, 11 апреля 2020 г.

Как узнать ip “белый” или нет?

#boost #cpp

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

1) Узнать локальный ip, и распрасив его, узнать не попадает ли он под локальный диапазон

10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
127.0.0.0/8


Локальный ip определятся так:

boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query(boost::asio::ip::host_name(), "");
boost::asio::ip::tcp::resolver::iterator it = resolver.resolve(query);
boost::asio::ip::tcp::endpoint endpoint = *it;
std::cout << endpoint.address().to_string() << '\n';


Является ли этот способ универсальным для всех видов (большинства) подключений к
Интернет?

2) Узнать еще и внешний ip и сравнить его с локальным.

3) И последний способ. Этот код я скачал с stackoverflow:

try {
    boost::asio::io_service netService;
    udp::resolver   resolver(netService);
    udp::resolver::query query(udp::v4(), "google.com", "");
    udp::resolver::iterator endpoints = resolver.resolve(query);
    udp::endpoint ep = *endpoints;
    udp::socket socket(netService);
    socket.connect(ep);
    boost::asio::ip::address addr = socket.local_endpoint().address();
    std::cout << "My IP according to google is: " << addr.to_string() << std::endl;
 } catch (std::exception& e){
    std::cerr << "Could not deal with socket. Exception: " << e.what() << std::endl;

 }


здесь насколько мне говорит мой английский - подключение возможно только в том случаи
если у меня "белый" ip. И если подключения будет удачным - то ip "белый". Правильно
ли я понял? И какой вариант выбрать? 

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


Ответы

Ответ 1



Просто отбросит все локальные адреса -> 3. Сообщит Вам - внешний айпи если подключение сотоится. Вот тот, внешний айпи может быть как серым(NAT) так и - белым(BI-NAT). Отличие в том, будет ли Ваша машина "видна" по этому адресу из интернет. А для того чтоб это проверить - надо чтобы удалённое устройство послало пакет на открытый на вашей машине порт. И этот пакет должен успешно дойти. Увы, я не знаю операторов которые предлагают апи для отправки пакетов. Но есть вебсайты которые могут просканировать заданный порт. Разумеется, надо открыть этот порт и ждать на него пакета. К примеру: http://www.yougetsignal.com/tools/open-ports/

пятница, 13 марта 2020 г.

Обвертка над итератором для преобразования типа

#cpp #stl #boost #итераторы


Есть два типа A и B, между объектами этих типов установлено соответствие. Есть набор
X значений типа А и итератор по этому набору. Необходимо с минимальными трудозатратами
на основе итератора X получить итератор по значениям типа B.

В общем по-простому говоря, есть контейнер типа A, из итератора А нужно получить
итератор B, потому что клиент принимает итераторы B.

Например:

typedef std::pair A;
typedef float                 B;

B A2B(const A & item) // соответствие между A и B
{
    return item.second;
}

std::vector X;
input_iterator bi = magic_iterator_wrapper(A2B, X.begin());


Чем может быть magic_iterator_wrapper?
    


Ответы

Ответ 1



Можно использовать boost::transform_iterator #include #include #include #include using A = std::pair; using B = float; float A2B(const A & item) { return item.second; } template auto make_transform_iterator(I i, F f) { return boost::transform_iterator{i, f}; } int main() { std::vector
v = {{0, 1}, {0, 20}}; auto first = make_transform_iterator(v.begin(), A2B); auto last = make_transform_iterator(v.end(), A2B); std::cout << std::accumulate(first, last, 0) << '\n'; } >>> То же на реальном компиляторе <<<

libpthread.so.0: error adding symbols: DSO missing from command line

#cpp #unix #boost #codeblocks


Пытаюсь использовать message_queue. Собственно код ничего не делает, кроме вызова
конструктора. 
Часть build log:


  /usr/bin/ld: obj/Debug/main.o: неопределённая ссылка на символ «pthread_condattr_setpshared@@GLIBC_2.2.5»
  //lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from
command line
  collect2: error: ld returned 1 exit status


using namespace boost::interprocess;

int main()
{ 
    message_queue(open_or_create,"message_queue" ,100 ,100);
}


Что происходит?
    


Ответы

Ответ 1



.../libpthread.so.0: error adding symbols: DSO missing from command line вот тут советуют добавить опцию -lpthread к опциям компилятора. для того, чтобы скомпилировать ваш пример, мне пришлось добавить первой строкой: #include и передать компилятору опции -lrt -lpthread. тогда программа test из файла test.cpp компилируется удачно: $ CPPFLAGS="-lrt -lpthread" make test g++ -lrt -lpthread test.cpp -o test описанная же вами ошибка легко воспроизводится при удалении опции -lpthread: $ CPPFLAGS="-lrt" make test g++ -lrt test.cpp -o test /usr/bin/ld: /tmp/cc8fR82D.o: undefined reference to symbol 'pthread_condattr_setpshared@@GLIBC_2.2.5' //lib/x86_64-linux-gnu/libpthread.so.0: error adding symbols: DSO missing from command line collect2: error: ld returned 1 exit status : recipe for target 'test' failed make: *** [test] Error 1

понедельник, 9 марта 2020 г.

Узнать имя класса во время исполнения программы

#cpp #boost


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

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

Это можно реализовать? Можно и boost`ом.
    


Ответы

Ответ 1



Функция typeid(object) возвращает объект типа type_info с информацией о типе объекта, для которого она вызывается (собственно, о классе). У этого объекта(type_info) есть метод name(), который и возвращает имя класса. Для использования нужно подключить . Так что в итоге для получения имени класса нужно использовать typeid(*this).name() Или, если функция не является методом класса, передать в typeid сам объект или разыменованный указатель.

Ответ 2



Собственные средства c++ не позволяют ничего узнать о контектсте вызова функции. Стек вызова можно проанализировать с помощью некоторых внешних библиотек / API операционной системы. Например: http://man7.org/linux/man-pages/man3/backtrace.3.html https://msdn.microsoft.com/en-us/library/ms680650%28VS.85%29.aspx . Но для применение этих средств может налагать дополнительные ограничения на собираемый код (сборка с отладочной информацией и т.д.). Более разумно, ИМХО, передавать контекст вызова явно: это позволит использовать "штатные" средства c++: __FILE__, __LINE__, __func__, .... Извлечение контекста можно замаскировать макросом: struct Context{ const char* file; unsigned line; const char* function; }; // Work around MSVS #ifndef __func__ #define __func__ __FUNCTION__ #endif #define CONTEXT() Context({ __FILE__, __LINE__, __func__}) void foo( int a, int b, Context c ); #define FOO(a, b) foo( a, b, CONTEXT() ) Имя класса можно извлечь либо разобрав имя __FUNCTION__, либо из указателя this : typeid(this).name() Но не хочется включать этот обращение к this в макрос, поскольку функции может быть вызвана из контекста, статической функции. С другой стороны для целей разделения логов может быть достаточно __FILE__.

воскресенье, 8 марта 2020 г.

Явное приведение типов и lexical_cast

#cpp #boost


Пример кода:

#include 
#include 

struct A
{
    int a;
    explicit operator std::string() {
        return "<" + std::to_string(a) + ">";
    }

};

void f(std::string s) {
    std::cout << s << std::endl;
}

int main()
{
    A a {1};
    f(static_cast(a));
    f(std::string(a));
    f((std::string)a);
    // f(boost::lexical_cast(a));
    return 0;
}


Вопроса 2:

1) Можно ли запретить приведение типов в С-стиле, так, чтобы на вызов f(std::string(a))
или f((std::string)a) компилятор ругался, а f(static_cast(a)) работало?

2) Как "научить" срабатывать вызов f(boost::lexical_cast(a))?
    


Ответы

Ответ 1



Что касается запрета приведения в стиле C, могу только посоветовать посмотреть в сторону опций компилятора. В GCC, например, можно добавить флаги -Wold-style-cast и -Werror, тогда приведение в стиле C будет вызывать предупреждение, а предупреждения будут трактоваться как ошибки. С boost::lexical_cast все просто, нужно добавить специализацию шаблона. Ваш пример примет такой вид: #include #include #include struct A { int a; explicit operator std::string() const{ return "<" + std::to_string(a) + ">"; } }; namespace boost{ template<> std::string lexical_cast(const A& arg){ return std::string(arg); } }; void f(std::string s) { std::cout << s << std::endl; } int main() { A a {1}; f(static_cast(a)); f(std::string(a)); f((std::string)a); f(boost::lexical_cast(a)); return 0; }

Ответ 2



Сообразил некое костыльное решение для запрета преобразований без static_cast: #include #include template T my_cast(X&& x) { return static_cast(x); } #define static_cast my_cast struct A { int a; private: explicit operator std::string() const { return "<" + std::to_string(a) + ">"; } template friend T my_cast(X&& x); }; void f(std::string s) { std::cout << s << std::endl; } int main() { A a {1}; f(static_cast(a)); // ok f(std::string(a)); // error f((std::string)a); // error return 0; }

среда, 4 марта 2020 г.

Как использовать сопрограммы С++ с Boost.Asio?

#cpp #boost #сопрограмма


Есть прокси-сервер, написанный на асинхронном API Boost.Asio - async_* функции и
коллбеки.

Полный код есть в этом ответе.
Схематично его можно описать так:

Цикл приема входящих соединений, установка соединения с сервером назначения:

void accept_loop() {
   acceptor.async_accept(src_socket, [](auto err) {
     accept_loop();  // повторяем accept

     dst_socket.async_connect(dst_endpoint, [](auto err) {
       proxy_loop(src_socket, dst_socket);
       proxy_loop(dst_socket, src_socket);
     });
  });
}


Цикл передачи данных, по 2 шт. на каждое соединение:

void proxy_loop(socket src, socket dst) {
  async_read(src, buf, [](auto error, auto n) {
    async_write(dst, buf, [](auto error, auto n) {
      proxy_loop(src, dst);  // повторяем read
    });
  });
}


Как переписать этот сервер с использованием сопрограмм С++?
    


Ответы

Ответ 1



В настоящий момент нет стандартного класса Future, который был бы совместим с сопрограммами и co_await. Также Boost.Asio еще не поддерживает co_await из коробки. Поэтому мы напишем и то и другое, всего за сто строк кода. Начнем с универсального Future, который можно вернуть из сопрограммы, и который можно ждать в co_await. template struct Future { // На памяти экономить не будем, // поэтому данные будем хранить в некотором "общем состоянии", // результат будем копировать (или перемещать). struct SharedState { T value; std::experimental::coroutine_handle<> h; std::atomic is_ready; }; // Поддержка использования Future как результата сопрограммы. struct promise_type { std::shared_ptr s = std::make_shared(); Future get_return_object() { return {s}; } std::experimental::suspend_never initial_suspend() { return {}; } // SharedState переживет удаление promise_type в конце работы сопрограммы std::experimental::suspend_never final_suspend() { return {}; } void return_value(T value) const { s->value = std::move(value); if (s->is_ready.exchange(true)) s->h.resume(); } }; std::shared_ptr s; // Поддержка co_await. bool await_ready() noexcept { return false; } bool await_suspend(std::experimental::coroutine_handle<> h) noexcept { s->h = h; return !s->is_ready.exchange(true); } T await_resume() { return std::move(s->value); } }; Теперь пишем всё то же самое, но для случая когда сопрограмма не возвращает значений. template<> struct Future { struct SharedState { std::experimental::coroutine_handle<> h; std::atomic is_ready; }; struct promise_type { std::shared_ptr s = std::make_shared(); Future get_return_object() { return {s}; } std::experimental::suspend_never initial_suspend() { return {}; } std::experimental::suspend_never final_suspend() { return {}; } void return_void() const { if (s->is_ready.exchange(true)) s->h.resume(); } }; std::shared_ptr s; bool await_ready() noexcept { return false; } bool await_suspend(std::experimental::coroutine_handle<> h) noexcept { s->h = h; return !s->is_ready.exchange(true); } void await_resume() {} }; Это был наш Future. Теперь пишем обертки над async_* функциями Boost.Asio. Мы можем использовать тот же Future::promise_type, как будто это сопрограмма. Future coro_accept(boost::asio::ip::tcp::acceptor& acceptor, boost::asio::ip::tcp::socket& socket) { Future::promise_type p; acceptor.async_accept(socket, [p](auto error) { p.return_value(error); }); return p.get_return_object(); } Future coro_connect(boost::asio::ip::tcp::socket& socket, boost::asio::ip::tcp::endpoint endpoint) { Future::promise_type p; socket.async_connect(endpoint, [p](auto error) { p.return_value(error); }); return p.get_return_object(); } Если callback принимает больше одного параметра, то их можно сделать out-параметрами. В С++17 можно будет использовать tuple и structured bindings для распаковки. template Future coro_read(boost::asio::ip::tcp::socket& socket, Buffers bufs, std::size_t& bytes_read) { Future::promise_type p; socket.async_read_some(bufs, [p, &bytes_read](auto error, auto n) { bytes_read = n; p.return_value(error); }); return p.get_return_object(); } template Future coro_write_all(boost::asio::ip::tcp::socket& socket, Buffers bufs, std::size_t& bytes_written) { Future::promise_type p; async_write(socket, bufs, boost::asio::transfer_all(), [p, &bytes_written](auto error, auto n) { bytes_written = n; p.return_value(error); }); return p.get_return_object(); } И наконец сам код сервера, те же ~50 строк что и в оригинале boost::asio::io_service io_service; boost::asio::ip::tcp::resolver resolver(io_service); boost::asio::ip::tcp::resolver::query dst_query("arrowd.name", "80"); boost::asio::ip::tcp::resolver::iterator dst_iterator = resolver.resolve(dst_query); boost::asio::ip::tcp::endpoint dst_endpoint = *dst_iterator; Future proxy(boost::asio::ip::tcp::socket& src, boost::asio::ip::tcp::socket& dst) { char buf[4096]; for (;;) { std::size_t bytes_read; auto error = co_await coro_read(src, boost::asio::buffer(buf), bytes_read); std::cout << "read " << bytes_read << ' ' << error << '\n'; if (error) break; std::size_t bytes_written; error = co_await coro_write_all(dst, boost::asio::buffer(buf, bytes_read), bytes_written); std::cout << "write " << bytes_written << ' ' << error << '\n'; if (error) break; } // Закрытие сокетов вызовет ошибку в сопрограмме которая качает в другую сторону src.close(); dst.close(); } Future connect(boost::asio::ip::tcp::socket src) { boost::asio::ip::tcp::socket dst(io_service); auto error = co_await coro_connect(dst, dst_endpoint); std::cout << "connect " << error << '\n'; if (error) co_return; auto _ = proxy(src, dst); // Запускаем первую сопрограмму без ожидания co_await proxy(dst, src); // Запускаем вторую и ждем // Мы вышли из второй с какой-то ошибкой co_await _; // Ждем завершения первой } Future accept_loop() { boost::asio::ip::tcp::endpoint src_endpoint( boost::asio::ip::address_v4::loopback(), 8080); // localhost:8080 boost::asio::ip::tcp::acceptor acceptor{io_service, src_endpoint}; for (;;) { boost::asio::ip::tcp::socket src(io_service); auto error = co_await coro_accept(acceptor, src); std::cout << "accept " << error << '\n'; if (error) co_return; connect(std::move(src)); } } int main() { accept_loop(); io_service.run(); // Можно запустить параллельно в нескольких потоках } Код стал гораздо чище - пропали коллбеки. connect пришлось вынести из accept_loop в отдельную функцию, т.к. это отдельная сопрограмма. Всё что явно выделялось в динамической памяти теперь выглядит как локальные переменные. При этом оно всеравно живет в динамической памяти, в coroutine-state. Это позволяет передавать сокеты в proxy по ссылке - их время жизни привязно к connect. В данном коде отсутствует обработка исключений. Для поддержки исключений, Future должно уметь перебрасывать исключения при помощи std::exception_ptr.

четверг, 13 февраля 2020 г.

C++ быстрые регулярные выражения

#cpp #регулярные_выражения #boost


нужно прогнать по тексту 2000 строк 200 регулярных выражений на совпадение.
какой движок дает наибольшую скорость ?

как оптимизировать настройку регулярных выражений С++ ?
    


Ответы

Ответ 1



Нужно различать скорость в среднем и скорость в худшем случаях. Большинство современных движков регулярных выражений основаны на переборе с возвратом, поэтому в патологических случаях они работают за экспоненциальное время, и одно простое регулярное выражение на одной строке в какую-то сотню символов не завершится при вашей жизни. Но, хочу заметить, что такие патологические случаи в реальной жизни маловероятны. С другой стороны, если у вас нет контроля над входными данными (то есть они, например, поступают извне, от конечного пользователя), злоумышленник вполне может сделать такой случай реальностью. Есть другой класс движков, ныне мало распространенных в библиотеках языков программирования, которые основаны на эмуляции конечных автоматов. Наиболее современная из относительно распространенных сейчас -- re2 от Russ Cox. Более того, есть отличная статья от него же, которая раскрывает суть проблемы реализации движков регулярных выражений с помощью перебора с возвратом. Но опять же, на ваших данных эта библиотека может работать хуже, чем обычные движки, тут скорее вопрос, контролируете ли вы эти данные или нет. Ну и стоит учесть, что в re2 не реализованы обратные ссылки (их добавление делает язык, описываемый "регулярными выражениями", не регулярным, поэтому эмуляцией конечных автоматов тут не обойтись). В общем все довольно очевидно. Если вы полностью контролируете свои данные, то просто выберите пару-тройку самых распространенных библиотек, и проведите тестирование, какая работает лучше на ваших данных. Если же за входными данными контроля нет, то лучше выбрать ту же re2, которая эмулирует конечный автомат гарантированно работает за полиномиальное, а не экспоненциальное время.

воскресенье, 2 февраля 2020 г.

Запустить Python класс из кода C++

#cpp #python #linux #boost #python_c_api


Допустим, что объявлен простой класс на языке Python в файле example.py:

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def print_position(self):
        return "X: {0} Y: {1}\n".format(self.x, self.y)


Как вызвать метод print_position из кода написанного на C++?

main.cpp:

#include
#include 

int main()
{
     Py_Initialize();
     // ... CODE HERE
     Py_Finalize();
}


Смотрел в сторону Cyhton и Boost/Python, но точно разобраться не получилось.
Ссылки в которых что-то нашел: 


Первая ссылка 
Вторая ссылка

    


Ответы

Ответ 1



Отсюда (с небольшими правками под Python 3.5 и дополнительными проверками) #include int main(int argc, char *argv[]) { PyObject *pName, *pModule, *pDict, *pClass, *pInstance, *pValue; int i, arg[8]; if (argc < 4) { fprintf(stderr, "Usage: call module_name class_name method_name [arguments]\n"); return 1; } Py_Initialize(); // Преобразуем первый аргумент в unicode-строку python pName = PyUnicode_DecodeFSDefault(argv[1]); // Импортируем модуль // import module_name pModule = PyImport_Import(pName); Py_DECREF(pName); // Освобождаем ссылку на строку с именем модуля if (pModule != NULL) { // Получаем пространство имён (__dict__) модуля pDict = PyModule_GetDict(pModule); // Получаем класс class_name pClass = PyDict_GetItemString(pDict, argv[2]); // Проверяем, что полученный class_name можно вызвать if (pClass && PyCallable_Check(pClass)) { // Получаем объект // obj = class_name() pInstance = PyObject_CallObject(pClass, NULL); if(pInstance != NULL) { // Подготавливаем параметры if(argc > 4) { for (i = 0; i < argc - 4; i++) { arg[i] = atoi(argv[i + 4]); } // Вызываем метод с двумя параметрами // value = obj.multiply2(3, 2) pValue = PyObject_CallMethod(pInstance, argv[3], "(ii)", arg[0], arg[1]); } else { // Вызываем метод без параметров // value = obj.multiply() pValue = PyObject_CallMethod(pInstance, argv[3], NULL); } if (pValue != NULL) { printf("Return of call : %d\n", PyLong_AsLong(pValue)); // Освобождаем ссылку на выделенную в памяти // переменную для возвращённого из метода результата Py_DECREF(pValue); } else { PyErr_Print(); } Py_DECREF(pInstance); // Освобождаем ссылку на объект } else { PyErr_Print(); } Py_DECREF(pClass); // Освобождаем ссылку на класс } else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find class \"%s\"\n", argv[2]); } Py_DECREF(pDict); // Освобождаем ссылку на пространство имён Py_DECREF(pModule); // Освобождаем ссылку на модуль } else { PyErr_Print(); } Py_Finalize(); return 0; } Как заметил @jfs, это только пример, код сильно упрощён для ясности. В боевом проекте на те же операции будет раза в два больше кода. Или в четыре, если понадобится многопоточность.

Ответ 2



При ответе на вопрос очень помогли ответы выше и ответы по ссылке. Структура проекта следующая: в одной папке лежат файлы main.cpp и example.py. Файл example.py: class point: def __init__(self, x, y): self.x = x self.y = y def print_position(self): return "X: {0} Y: {1}\n".format(self.x, self.y) Файл main.cpp: #include #include #include //#include int main(int argc, char* argv[]) { Py_Initialize(); // Для импорта созданного мною модуля необходимо передать // его имя как аргумент командной строки (у меня example.py) PySys_SetArgv(argc, argv); PyObject* module_name = PyString_FromString("example"); // Загрузка модуля (example.py) PyObject* module = PyImport_Import(module_name); if (module == nullptr) { PyErr_Print(); std::cerr << "Failed to import module\n"; return 1; } // Загрузка пространств имен модуля PyObject* dict = PyModule_GetDict(module); if (dict == nullptr) { PyErr_Print(); std::cerr << "Failed to import __dict__\n"; return 1; } // Загрузка класса из example.py std::string py_class_name = "point"; PyObject* py_class = PyDict_GetItemString(dict, py_class_name.c_str()); if(py_class == nullptr) { PyErr_Print(); std::cerr << "Failed import class " << py_class_name << std::endl; return 1; } // Поскольку конструктор принимает пару аргументов // То необходимо создать пару аргументов PyObject* py_arg_tuple = PyTuple_New(2); PyTuple_SetItem(py_arg_tuple, 0, PyInt_FromLong(5)); PyTuple_SetItem(py_arg_tuple, 1, PyInt_FromLong(10)); // Создание объекта класса point PyObject* obj; if (PyCallable_Check(py_class)) obj = PyObject_CallObject(py_class, py_arg_tuple); else std::cout << "Cannot instantiate the Python class" << std::endl; // Вызываем метод print_position класса point PyObject* val = PyObject_CallMethod(obj, "print_position", NULL); if (!val) PyErr_Print(); // конвертируем результат в std::string и печатаем std::string s (PyString_AsString(val)); std::cout << s; Py_Finalize(); return 0; } Для запуска под Ubuntu в консоли необходимо ввести команды: $ g++ main.cpp -std=c++11 -lpython2.7 $ ./a.out example P.S. аргумент командной строки совпадает с названием импортируемого и написанного мною Python модуля.

среда, 29 января 2020 г.

Как установить библиотеку boost?

#boost #cpp #библиотеки #visual_studio_2010


Доброго времени суток. Нужна помощь!

Хочу установить библиотеку boost. Во всех туториалах люди заходят в консольку Visual
Studio Command Prompt (2010). Но проблема в том, что я не могу ее найти! Искать в винде
пробовал - не нашел.

У меня экспресс-версия! Дело в этом?    


Ответы

Ответ 1



Библиотека boost - это набор частично компилируемых исходных кодов. В некоторых случаях ничего не нужно собирать, достаточно скачать с официального сайта дистрибутив, разместить в удобном месте и в настройках проекта указать пути. У меня в специальной папке, где я храню библиотеки многоразового использования, лежит подпапка boost_1_56_0. Рядом с нею еще ряд других более старых версий этой библиотеки. В переменные среды (в windows это там же, где и PATH) я заношу переменную BOOST_ROOT, которая указывает на последнюю сборку. Т.е. как появится новая, я создам папку boost_1_XX_Y и переназначу эту переменную. В настройках любых проектов мне достаточно указать $(BOOST_ROOT)\include - для доступа к headers, и $(BOOST_ROOT)\stage\lib32 / $(BOOST_ROOT)\stage\lib64 для доступа к конкретным библиотекам, нужной мне разрядности. Поскольку boost автоматически выдает имена собираемым библиотекам, с учетом компиляторов, которыми они собираются, даже если у Вас разные компиляторы, бинарники удобно сбрасывать в одну папку, как указано выше. С Intel Compiler у Вас получатся libboost_name-iw-type-version.lib. С Visual Studio у Вас получатся libboost_name-vcXX-type-version.lib, где XX - версия компилятора visual studio (не студии, а именно компилятора). С MinGW с gcc у Вас получится libboost_name-gcc-type-version.lib, если мне не изменяет память. При этом для сборки библиотек, которые необходимо собирать, необходимо выполнить одни и те же действия в консоли: Для Intel Compiler это будет в соответствующем Command Prompt. Для Visual Studio это будет в соответствующем Command Prompt. Для MinGW это будет в обычной консоли, если, конечно, путь к bin в MinGW у Вас добавлен в переменную среды PATH. Действия надо выполнить одни и те же. Сначала bootstrap.bat, а потом b2 --help. В хелпе b2 Вы увидите все варианты настройки сборки, чтобы собрать наиболее удобным Вам образом. Строчка будет выглядеть так: b2 параметр1 параметр2 параметр3 ... toolset - его стоит указать, чтобы сборка производилась конкретным компилятором (gcc, intel, visual studio), причем можно указать и версию компилятора. variant, вид сборки, debug или release. Для разработки Вам понадобятся оба варианта. link - Вы выбираете, будет Ваш бинарный код обращаться в dll или содержать "в себе" все используемые алгоритмы. threading - честно говоря, плохо понимаю смысл этой директивы и всегда указываю multi. Редко мы пишем однопоточные приложения. runtime-link - то же, что и link, только для рантайма. address-model - параметр не указан в хелпе, но помогает выбрать архитектуру собираемых библиотек. stage/install, отличаются лишь тем, что install позволит "выгрузить" только нужное в отдельную папку, stage собирает всё туда, где оно есть. Если Вы не увлекаетесь изменениями исходников boost, stage Вам вполне подойдет. В итоге получается что-то вроде вот такого: b2 toolset=vc120 variant=debug link=shared threading=multi runtime-link=shared address-model=32 stage Для дебага в x32 и такого: b2 toolset=vc120 variant=release link=static threading=multi runtime-link=shared address-model=32 stage Для релиза в x32. Ждете около 40 минут, радуетесь результату. В случае проблем - гуглите, скорее всего уже тысячи людей сталкивались с Вашей проблемой, и ее решение - невнимательность или какие-то специфические настройки чего-нибудь. Например, при сборке boost python вылезает много warning-ов на MinGW из-за конфликта хедеров, подобные вещи можно разрулить в частном порядке. Потратив 2 часа на то, чтобы один раз в этом разобраться, Вы никогда не будете зависеть ни от каких сторонних сборок, будете понимать, где у Вас что лежит, кто туда положил и т.п. В общем, это полезно. ЗЫ: Command Prompt находится в Visual Studio Tools. Или посмотрите в VStudio_PATH\CommonXX\Tools\VsDevCmd.bat

Ответ 2



Наверное, самый простой способ - это установить заранее собранный пакет под ваш компилятор. Брать тут.

Ответ 3



Есть программка, называется BlueGo, по адресу: ссылка. Это сборник различных библиотек в том числе и boost, но плюс ее в том, что там есть абсолютно все версии, начиная со старых и заканчивая новыми. Выбираешь версию, выбираешь чем компилировать, выбираешь состав сборки и ждешь окончания.

воскресенье, 12 января 2020 г.

Установка библиотеки boost в QtCreator

#boost #qt


Доброй ночи, уважаемые коллеги!

У меня возникла следующая проблема. Мне надо установить в Qt creatore библиотеку
boost. Я скачал библиотеку, установил ее в папке D:\boost_1_54_0. (Я пытаюсь сделать
как сказано здесь) Далее в теле программы в проекте .pro  я пишу следующие вещи.

TEMPLATE = app 
CONFIG += console    
CONFIG -= app_bundle    
CONFIG -= qt    
SOURCES += main.cpp    
win32 {   
    INCLUDEPATH += D:/boost_1_54_0       
    LIBS += -LC:/boost/lib \       
            -lboost_serialization-mgw46-mt-d-1_54 \       
            -lboost_filesystem-mgw46-mt-d-1_54 \      
            -lboost_system-mgw46-mt-d-1_54      
}


Когда же я пытаюсь скомпилировать код программы, мне выдается ошибка 

cannot find -lboost_filesystem-mgw46-mt-d-1_54

collect2: ld returned 1 exit status


Подскажите, пожалуйста, что я делаю не так? Либо,если это можно, скиньте, пожалуйста,
инструкцию, как устанавливать boost на Qt. 

Обновление

Вот смотрите, я правильно понял, что надо было в строке 

INCLUDEPATH += 


написать, где находится папка boost_1_54_0? Я ее скопировал в D. При этом сам QtSDK
находится на С. Мне надо скинуть всю папку boost_1_54_0 в какое то определенное место?
Я просто, честно говоря, не совсем еще понимаю,что нужно прописывать в либах. Я пытаюсь
прописать LD:/boost_1_54_0\lib, однако проблема не решается. Нужно ли какой-нибудь
дополнительный дистрибутив ставить?
    


Ответы

Ответ 1



-L"PATH" говорит что указывается папка где линковищик будет искать lib файлы. Проверьте что в папке C:/boost/lib которую вы указали лежит скомпиленая либа boost_filesystem-mgw46-mt-d-1_54.lib Вот пример указания где у меня лежит . Лень перекладывать было указал как есть. -L"C:\usr\lib\boost_1_57_0\bin.v2\libs\system\build\msvc-12.0\debug\architecture-ia64\link-static\threading-multi" -llibboost_system-vc120-mt-gd-1_57 P.S. Если вы просто распаковали сорцы, то и либы не будет. Надо все это дело собирать. Читайте доки как собрать в папке буста.

четверг, 9 января 2020 г.

Ошибка компиляции при виртуальном наследовании с использованием multi_index_container

#cpp #ооп #boost


#include 
#include 
#include 

namespace mi = boost::multi_index;


namespace vo {

class Statable {};

class Entity : virtual public Statable {};

} /* namespace vo */

class Statable : virtual public vo::Statable {};

class Entity
    : virtual public vo::Entity
    , public Statable
{
    std::string name_;

public:
    std::string const& name() const { return name_; }

private:
    typedef mi::multi_index_container <
      Entity*
    , mi::indexed_by > >
    > nested_objects_t;

    nested_objects_t nested_objects_;
};


Добрый день!
При такой архитектуре кода (это упрощенный пример, естественно), ломается компиляция
с ошибкой:


  Error C1001   An internal error has occurred in the compiler


Методом проб и ошибок выявлено, что проблема в строке:

mi::hashed_unique >


Данная проблема возникает только тогда, когда присутствует виртуальное наследование!
Т.е. если теперь Entity сделать только наследником от ::Statable (убрать virtual public
vo::Entity), а того в свою очередь сделать обычным наследником от vo::Statable (без
virtual public), то ошибка пропадет.

Также ошибка пропадет если просто вынести typedef за пределы класса Entity!

Почему это происходит, я не могу понять никак?

P.S.
Пробовал в VS2010, VS2012, VS2015 с различными версиями boost от 1.33 до 1.68
    


Ответы

Ответ 1



У меня эта ошибка воспроизвелась даже в более простом примере. Достаточно наличия виртуального наследования в верхнем классе Entity для ее возникновения. Базового класса достаточно одного, любого. Однако после добавления #pragma pointers_to_members(full_generality) ошибка исчезла. Я не проверял, приводит ли это к полноценной функциональности вашего кода, однако при работе с указателями на члены класса в условиях множественного и виртуального наследования в MSVC++ я бы рекомендовал всегда выставлять эти опции в наиболее "безопасный" вариант.

Ответ 2



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

пятница, 27 декабря 2019 г.

Может ли std::bind быть удобнее, чем лямбды?

#cpp #stl #boost #asio


Поковырялся с asio, и наткнулся вот на какой момент. Для привязки handle'ров к функциям
асинхронного чтения/записи используется bind: в примерах - из boost, я без особых проблем
заменил на stl. Например:

socket_.async_send_to(asio::buffer(*message), remote_endpoint_,
      boost::bind(&udp_server::handle_send, this, message,
        asio::placeholders::error,
        asio::placeholders::bytes_transferred));


И ниже по тексту:

void handle_send(boost::shared_ptr /*message*/,
  const asio::error_code& /*error*/,
  std::size_t /*bytes_transferred*/)  {  }


Но в процессе "щупания" изрядную часть кода я переписал (в первую очередь для отвязки
от boost - шеф и на header-only версию asio смотрит косо). И везде намного удобнее
было написать лямбду:

socket_.async_send_to(asio::buffer(*message),remote_endpoint_,
                    [](const asio::error_code&,std::size_t){});


Поэтому вопрос такой: есть ли какие-то области применения, где std::bind или boost::bind
остаётся единственным или более удобным способом, чем лямбда-выражения? Стоит ли подробно
изучать bind, или достаточно на пальцах представлять, чтобы при необходимости разобраться
в чужом коде - а в своём он не понадобится?
    


Ответы

Ответ 1



Насколько мне кажется, по удобству синтаксиса создание функциональных объектов через std::bind и тому подобное проигрывает по выразительности лямбдам. Тем не менее, я бы всё равно разобрался в том, как работает std::bind (и даже попробовал бы реализовать его вручную, чтобы прочувствовать проблематику). Знания карман не тянут. Кроме этого, возможное применение старой функциональности: чтение и понимание чужого кода программирование под среду, которая имплементирует старый стандарт не исключено, что на вашей платформе старый метод будет производительнее, и это будет критично для вашего кода

среда, 18 декабря 2019 г.

Класс для перечисления с заданным типом C++

#cpp #qt #boost


Если в С++ сделать 

enum operation = {plus, minus, mult, div};


То перечисляемые константы буду иметь тип operation.

А если мне нужно, чтобы переменная operation была типа char, и могла принимать только
значения {'+', '-', '*', '/'} ?

Есть ли в std/Qt/boost/другой библиотеке какой-либо контейнер, который позволяет
делать что-то в духе:

Enumeration operation = {'+', '-', '*', '/'};


Или необходимо писать велосипед самому? Может, что-то есть на эту тему из области
шаблонов проектирования?

Upd:
Или, еще лучше, если перечисляемые значения могут быть разного типа. К примеру, при
разборе строки с арифметическим выражением на токены, токен может быть типов Operator,
Operand или Bracket. Как-то так:

Enumeration Token = {op, num, br};

    


Ответы

Ответ 1



Нет, ни в C++, ни в boost&Qt такого контейнера нет. Нет его по одной простой причине — в C++ нет одного общего типа как, скажем в C#&Java. Поэтому, просто невозможно создать контейнер, который будет принимать разнородные данные. Можно, конечно, создать std::vector(QList и вариации) но это, мягко говоря, не лучшее решение, т.к. всё равно придётся «помнить», что там за данные, иначе извлечь их не получится. Поэтому, такие задачи решаются введением одного общего предка, а все вариации являются его наследниками. Создаётся контейнер указателей на этого предка и в него помещаются нужные потомки. К примеру, у Вас может быть std::vector> vec; и в него Вы будете помещать Ваши конкретные объекты: vec.push_back(std::make_shared()); Но лучше это всё вынести в ещё одну абстракцию, которая будет представлять собой контейнер, а не использовать «голый» вектор. Вообще, это довольно обширная тема, которую в рамках ответа не так просто и раскрыть. Но если просто взять Ваш пример из вопроса, то его можно легко написать так: std::tuple Token = std::make_tuple(op, num, br); Хотя, я полагаю, Вам всё же нужен будет более широкий функционал и динамизм, который не достижим с помощью std::tuple. Если моё предположение верно, то для Вас подходит первая часть ответа.

Ответ 2



Вашу проблему касательно char можно решить достаточно просто: хранить коды символов в самом enum'e (ведь базовым типом перечисления может быть любой целочисленным тип, т.е. int, long, char и некоторые другие), а затем при обращении к элементам перечисления делать явное приведение к char: enum CharEnum: char { plus = '+', minus = '-', mult = '*', div = '/' }; int main() { std::cout << static_cast(CharEnum::plus); } Стоит заметить, что начиная с C++11 появился enum class (можно писать и enum struct, что то же самое), который обладает рядом преимуществ перед обычным enum: не экспортирует свои значения в окружающую область видимости, что могло бы приводить к конфликтам имен отсутствует неявное преобразование значений перечислимого типа к целочисленным типам как у обычного enum, что могло бы приводить к ошибкам Таким образом, в предыдущем примере достаточно поменять enum на enum class, а весь остальный синтаксис останется неизменным.

воскресенье, 15 декабря 2019 г.

Ломаю голову, контроль пути

#cpp #boost #геолокация


Имеется прорисовка пути маршрута автобуса выполненная при помощи массива точек (широта,
долгота) которые затем через leaflet прорисовываются у клиента

Делалось это все просто для визуального отображения маршрута в GPS мониторинге.
Теперь понадобилось контролировать съезд с маршрута как можно менее затратно в плане
ресурсов.

План таков, преобразовать каким то образом трансформировать точки в полигон и уже
через boost::geometry::intersects сверять находится ли машина на маршруте или нет при
поступлении свежих координат.

Идея по трансформации только такая, брать 2 точки добавлять по 10 метров отступа
слева и создавать полигон.

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

Ломаю голову..

Может есть что то готовое для подобной задачи?

Прошу помощи
    


Ответы

Ответ 1



А зачем возиться с полигонами? У вас есть массив точек маршрута. Каждая пара соседних точек определяет отрезок, часть маршрута. Определить, к какому отрезку ближе всего точка Вычислить кратчайшее расстояние до него (длинна перпендикуляра) Если оно больше максимального заданного расстояния Lmax - зафиксировать съезд с маршрута

воскресенье, 8 декабря 2019 г.

Зачем нужен boost::weak_ptr?

#boost #cpp


Я относительно недавно работаю с boost и многое еще не понятно. Вопрос по указателям:
зачем нужны weak_ptr, что они дают такое, чего не дают smart_prt? Почему нельзя использовать
только smart_prt?     


Ответы

Ответ 1



Попробую объяснить "на пальцах". Вначале пример: shared_ptr a1( new A() ); // тут счетчик == 1, мы создали объект // и у нас один сильный указатель на него shared_ptr a2 = a1; // тут счетчик == 2 // мы имеем два сильных указателя на один объект weak_ptr w = a2; // тут счетчик снова == 2 // мы имеем два сильных указателя на один объект // и один слабый a2.reset(); // тут счетчик == 1, один сильный указатель теперь пустой // но объект A жив и доступен через сильный указатель a1 // так как на данный момент существует один сильный указатель // на объект (a1), то мы можем из слабого указателя получить сильный a2 = w.lock(); // тут счетчик == 2, // мы получили вполне работоспособный сильный указатель из слабого a2.reset(); a1.reset(); // тут счетчик == 0, нет ни одного сильного указателя // Объект автоматически умер при вызове a1.reset() // и попытка получить сильный указатель из слабого вернет пустой shared_ptr shared_ptr a0 = w.lock(); // вот это условие будет выполнено a0.get() == 0 Внутри сильный указатель представляет из себя структуру типа: struct storage { Object* pointer; long refs; } Когда создается новый сильный указатель поле refs увеличивается на единицу (++refs). Когда уничтожается сильный указатель поле refs уменшается на единицу (--refs). Когда refs равно нулю, уничтожается pointer. При создании слабого указателя просто копируется ссылка на эту структуру, но поле refs не изменяется. При операции lock() слабый указатель просто смотрит на поле refs: refs > 0 - создаетя новый сильный указатель и поле refs увеличивается на 1 refs == 0 - возвращается пустой сильный указатель

Ответ 2



Слабые указатели нужны, в том числе, для предотвращения циклических ссылок. Если два объекта ссылаются друг на друга «жестко», очевидно, они не удалятся, если не предпринять дополнительных действий («разорвать» цикл, удалить один из элементов цикла вручную). Обычно смотрят кто кем владеет и если объект A не владеет объектом B, то он хранит мягкий указатель на B. Для примера: некий список владеет элементами и хранит жесткие указатели на них. Элементы могут хранить мягкий указатель на список, чтобы, например, удалить себя из него. Если бы все указатели были жесткими, пришлось бы совершать дополнительные действия при удалении списка: удалять все его элементы (предположим, что у нас самописный список, а не из STL).

Ответ 3



«Слабые» указатели используются, вообще, не только в Бусте. Это очень распространённая концепция. Ведь smart_ptr и weak_ptr — это попытка реализовать что-то вроде сборки мусора. Объект существует до тех пор, пока на него есть хотя бы один указатель. Правильнее сказать, хотя бы один неслабый указатель. Самый распространённый и очевидный пример — кэширование. Если у Вас в кэше сохранится сильный указатель, то он не даст объекту удалиться. Обычно, всё-таки, хочется, чтобы в определении времени жизни объекта кэш не участвовал. wiki://Слабая ссылка wien://Weak reference

Использование STL и фреймворков в C++

#cpp #boost


C++ 2 главные библотеки (ИМХО): одна стандартная, одна почти стандартная(Boost).
Но ни одна из них не позволяет создавать GUI (с кнопочками, менюшками и т.д.). А потому,
есть и фреймворки: Qt, wxWidgets (MFC не в счет -- он не кроссплатформенен), однако,
оба "переписывают" (дублируют) Stl (QString, wxString и т.д.**; **вариант: компилировать
wxWidgets с использованием stl сами разработчики считают не совсем кошерным, т.к. он,
по умолчанию, не работает). 

Однако, в более-менее большом приложении обязательно будут сторонние библиотеки,
которые используют Stl, да и не обязательно все классы из Stl присутствуют в фреймворке.

Вопросы: что делать с огромным количеством абсолютно бесполезно продублированного
кода? Использовать stl классы или классы, предоставляемые фреймворком?

UPD: Что делать с принципом DRY (Don't Repeat Yourself) или это исключение?

UPD2: Как я понял, классы Qt серьёзно отличаются от аналогов в stl, а значит и DRY
страдает не сильно. Что про wxWidgets? В wxBook написано:


  Классы из wx на 70% похожи на классы из
  Stl


Я думаю, даже больше. А что с ними и DRY? 
    


Ответы

Ответ 1



Лично мое мнение - вопрос надуманный, поскольку концепция DRY (особенно в отношении контейнеров) намного в меньшей степени применима к устоявшимся фреймворкам, которые пережили несколько мажорных версий. Тем более, что, например, если говорить о STL, Qt и Boost, то DRY, за исключением некоторых моментов, не нарушается. Все Qt поддерживают семантику copy-on-write, а иногда вообще не совпадают с контейнерами STL в плане соответствия названия и назначения (например, std::set и QSet - это совершенно разные вещи). boost-specific контейнеры нигде не дублируют функциональность, предоставляемую STL. На уровне таких проектов решение о реализации своего набора контейнеров может быть принято чуть ли не исходя из аргументов типа "std::vector плохо смотрится в коде вместе с QString". Или, например, если вы подойдете и спросите у девелоперов EASTL, слышали ли они про DRY и способы уменьшения энтропии в системы, то, мне кажется, вам рассмеются в лицо :) Количество продублированного кода, как вы это называете, не является проблемой до тех пор, пока под кодом подразумевается код фреймворка. Для своих проектов полезно выбрать некоторую стратегию использования контейнеров, например, используем только QXyz или std::xyz. Причем понятно, что под использованием здесь имеется ввиду использование в интерфейсах. На уровне реализаций конкретных интерфейсов можете использовать что угодно и как угодно, хоть std::vector, написанный на ассемблере. Но протаскивать его вверх по иерархии, естественно, не стоит - достаточно в местах, где контейнер является частью интерфейса, осуществить соответствующее преобразование. Аргумент про размер выходного файла и время компиляции, тоже, на мой взгляд, несколько оторван от реальности. По своему опыту скажу, что приложение с boost::graph замечательно работает под ARM, а подключение любого хэдера boost::mpl сравнимо с подключением всех контейнеров Qt в плане времени компиляции. На практике, лично с моей точки зрения, 90% промышленных продуктов можно построить, если использовать Qt (фреймворк вообще и пользуясь только их контейнерами), boost::type_traits и . При необходимости также можно strip'ать и добавлять в проект какие-либо части boost типа boost::graph, boost::flyweight или boost::heap.

Ответ 2



Ну, вообще то, boost и stl - это статик либы. Так что если Вы не будете из них ничего использовать, и добавляться к получаемому файлу из них не будет. А если вы будете использовать какой то элемент из буста, к примеру, то к вам добавится он и всё что ему требуется для функционирования, но всё остальное - нет.

суббота, 30 ноября 2019 г.

Не пойму, что делает функция bind() в коде

#cpp #boost #asio


Здравствуйте, недавно начал изучать Boost.Asio, и в одном из примеров не могу понять,
что делает функция bind() в коде данной программы.
Если кто знает, для чего придназначена эта функция, пожалуйста, объясните. Так как
я недавно начал углубляться в библиотеку STL и Boost. Заранее спасибо.

io_service service;
size_t read_complete(char * buff, const error_code & err, size_t bytes) 
{
    if ( err) return 0;
    bool found = std::find(buff, buff + bytes, '\n') < buff + bytes;
    // we read one-by-one until we get to enter, no buffering
    return found ? 0 : 1;
}

void handle_connections() 
{
    ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),8001));
    char buff[1024];
    while ( true) 
    {
        ip::tcp::socket sock(service);
        acceptor.accept(sock);
        int bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2));
        std::string msg(buff, bytes);
        sock.write_some(buffer(msg));
        sock.close();
    }
}

int main(int argc, char* argv[]) 
{
    handle_connections();
}

    


Ответы

Ответ 1



std:bind (я буду говорить об std::bind, так как эта функция перенесена в стандарт C++ из boost) - это адаптер функциональных объектов, который позволяет адаптировать функциональные объекты под заданное число параметров. Чтобы было более ясно ее назначение, допустим вы решили написать функцию вывода произвольного целочисленного массива в поток std::cout . Ваша программа могла бы выглядеть следующим образом: #include void display( const int a[], size_t n ) { for ( size_t i = 0; i < n; i++ ) { std::cout << a[i] << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; display( a, N ); } Вывод программы на консоль: 1 2 3 4 5 6 7 8 9 10 Но затем вам пришла в голову идея усовершенствовать функцию вывода таким образом, чтобы она не просто выводила элементы массива, как они есть, но и делала какие-то с ними преобразования перед выводом. Поэтому вы решили добавить параметр в функцию, который будет представлять некоторую операцию над элементами массива. Этот третий параметр вы объявили как функцию с одним параметром. Используя эту функцию, вы можете, например, вывести на консоль удвоенные значения элементов массива с помощью лямбда-выражения: #include void display( const int a[], size_t n, int operation( int ) ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; auto doubled = []( int x ) { return 2 * x; }; display( a, N, doubled ); } Вывод на консоль будет 2 4 6 8 10 12 14 16 18 20 В данном случае возможно использовать лямбда-выражение doubled, потому что оно может быть неявно преобразовано в функцию с одним параметром, так как не имеет замыкания. Но, допустим, вы решили выводить на печать не только удвоенные значения элементов массива, но и значения элементов, умноженных на некоторый коэффициент, который задается во время выполнения программы. В этом случае ваша программа могла бы выглядеть следующим образом #include void display( const int a[], size_t n, int operation( int ) ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int factor = 2; auto multiplies = [&factor]( int x ) { return factor * x; }; display( a, N, multiplies ); factor = 3; display( a, N, multiplies ); } Однако она не будет компилироваться. Проблема заключается в том, что если лямбда-выражение содержит замыкание, auto multiplies = [&factor]( int x ) { return factor * x; }; ^^^^^^^^^ то оно не имеет неявного преобразования в указатель на функцию. Поэтому придется написать шаблонную функцию, которая будет иметь шаблонный параметр операции и в качестве аргумента может принимать не только функции, но и объекты классов, который имеют оператор вызова функции operator (). Ваша программа будет выглядеть следующим образом: #include template void display( const int a[], size_t n, Operation operation ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int factor = 2; auto multiplies = [&factor]( int x ) { return factor * x; }; display( a, N, multiplies ); factor = 3; display( a, N, multiplies ); } Вывод на консоль соответственно будет: 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 Но теперь, допустим, вы решили вывести на печать квадраты значений элементов массива. Вы опять бы могли использовать лямбда-выражение, как, например, auto_square = []( int x ){ return x * x; }; И ваша программа успешно бы выполнилась, используя это лямбда-выражение. Но вы знаете, что стандарт уже имеет функциональный объект std::multiplies, который осуществляет операцию x * y для двух аргументов своего оператора вызова функции operator (). Как им воспользоваться? Оператор этого функционального объекта принимает два аргумента, тогда как в нашей функции display функциональный объект принимает только один аргумент. Для этого используются функциональные адаптеры и, в частности, std::bind. Он может "превратить" функциональный объект std::multiplies из объекта, который принимает два аргумента в объект, который принимает один аргумент. Программа будет выглядеть следующим образом: #include #include template void display( const int a[], size_t n, Operation operation ) { for ( size_t i = 0; i < n; i++ ) { std::cout << operation( a[i] ) << ' '; } std::cout << std::endl; } int main() { const size_t N = 10; int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; auto square = bind( std::multiplies(), std::placeholders::_1, std::placeholders::_1 ); display( a, N, square ); std::cout << std::endl; } Вывод на консоль будет 1 4 9 16 25 36 49 64 81 100 Внутри функции display объект square вызывается как square( a[i] ), который в свою очередь делегирует работу объекту класса std::multiplies, вызывая его как std::multiplies()( a[i], a[i] ); Конечно конструктор std::multiplies не вызывается, так как объект данного класса уже был создан, когда создавался объект square auto square = bind( std::multiplies(), std::placeholders::_1, std::placeholders::_1 ); Вызывался только operator () этого объекта. В вашем примере вызов read требует в качестве третьего аргумента функциональный объект, который принимает только два аргумента: int bytes = read(sock, buffer(buff), boost::bind(read_complete,buff,_1,_2)); Однако, вы хотите, чтобы вызывалась функция read_complete, которая принимает три аргумента вместо двух. В этом случае используется адаптер bind, который сам получает два аргумента, как того требует вызов функции read, но делегирует работу функции read_complete, передавая ей три аргумента: два своих, обозначенных как _1 и _2, и которые ей передает внутри своего тела функция read, когда ее вызывает, и один дополнительный, buff, который она сохранила внутри себя, когда использовалась в качестве аргумента вызова read.

Ответ 2



bind это немного интересная функция (не путать с функцией работы сокетами, которая также называется bind!!!). Ее задача создать одну функцию на базе другой, подставив часть аргументов (это называется "частичным применением"). Посмотрим глубже. Функция read требует трех параметров - сокета, буфера для данных и "функции завершения" (вообще то у этой бустовой read всего восемь различных перегруженных вариантов). Эта функция будет вызываться, что бы определить, что данных прочитано достаточно. Там же в справке приведена сигнатура этой функции. У нее два параметра: код ошибки последнего чтения и сколько байт прочитано. Но что делать, если нет подходящей функции? Для этого есть bind. Он первым аргументом принимает функцию (в данном случае read_complete, а дальше аргументы. Так как исходная функция read_complete имела три аргумента, а функции read нужно только два, то один аргумент подставляется явно (первый), а вместо двух других - заглушки специального вида. В результате, когда функции read нужно будет вызвать функцию завершения, она вызовет read_complete, первый аргумент ей подставит bind, а два вторых будут подставлены с аргументов. Если попробовать написать код схематически, то все выглядит где то так io_service service; size_t read_complete(char * buff, const error_code & err, size_t bytes) { if ( err) return 0; bool found = std::find(buff, buff + bytes, '\n') < buff + bytes; // we read one-by-one until we get to enter, no buffering return found ? 0 : 1; } char buff[1024]; size_t new_func(const error_code & err, size_t bytes) { return read_complete(buff, err, bytes); } void handle_connections() { ip::tcp::acceptor acceptor(service, ip::tcp::endpoint(ip::tcp::v4(),8001)); while ( true) { ip::tcp::socket sock(service); acceptor.accept(sock); int bytes = read(sock, buffer(buff), new_func); std::string msg(buff, bytes); sock.write_some(buffer(msg)); sock.close(); } } Как видите, мне пришлось сделать буфер глобальным, что бы новоиспеченная функция имела к нему доступ. На самом деле переменная глобальной не становится, но это был самый простой способ эмулировать поведение. Эта функция bind пришла в с++ с функциональных языков вида haskell и лисп, где подобное встречается на каждом шагу.

среда, 27 ноября 2019 г.

Умные указатели shared_ptr и weak_ptr, отношение родитель и потомок

#c++ #boost #c++11


Насколько я понимаю, типичное использование weak_ptr такое:

class Child {
    private :
        weak_ptr mParent ;
} ;


class Parent: public enable_shared_from_this  {
    private :
        shared_ptr  mChild ;
};


Т.е. родитель владеет потомком и потомок будет уничтожен при уничтожении родителя.

А существуют ли случаи, когда удобнее делать наоборот?

class Child {
    private :
        shared_ptr mParent ;
} ;


class Parent: public enable_shared_from_this {
    private :
        weak_ptr  mChild ;
};

    


Ответы

Ответ 1



Конечно бывают. В основном когда родитель представляет собой большой ресурс, а дети представляют доступ к частям этого ресурса. При этом освобождение ресурса выполняет родитель. Например, под этот случай подойдет куча из WinApi. Родитель будет содержать дескриптор кучи, а дочерние объекты - указатели на блоки памяти выделеные из этой кучи. Если мы освободим объект кучи, то указатели станут невалидными, поэтому необходимо следить за порядком освобождения. Собственно, логично возложить эту работу на стандартные классы. Кстати, у std::shared_ptr есть конструктор, (номер 8 по ссылке) ориентированный на подобный шаблон: template< class Y > shared_ptr( const shared_ptr& r, T * ptr ); Здесь предполагается что r - это родитель, а ptr - ребенок. При этом доступ будет предоставлен к указателю ptr, но при удалении умного указателя будет освобождён r. Вот примерный сценарий: struct Parent { int Child; }; auto parent = std::make_shared(); auto child = std::shared_ptr(parent, &parent->Child); parent->Child = 10; bool is_ten = *child == 10; // OK parent = nullptr; // Parent не удален, он продолжает жить в child *child = 11; // OK child = nullptr; // а вот теперь Parent удаляется

понедельник, 25 ноября 2019 г.

Просьба code review: расширение boost для хранения настроек


Перед вами код моего расширения к библиотеке boost, который играет роль, схожую с классом QSettings в Qt, разве что реализовано все более в духе boost. 

Поддерживает произвольные значения для полей, их сериализацию и строгую visitor-based поддержку типов.

Хотелось бы услышать замечания по коду, что можно было сделать лучше и т.п, короче, устройте мне code review. Если кому-то интересно, то могу запостить код юнит-тестов.



#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include "core_defines.h"
#include "core_extensions_lexical_cast.h"

namespace boost {
namespace property_tree {

// Generic value holder, which will attempt to cast objects rather than
// indicate error if wrong type was supplied. This behavior is likely to
// be more error-prone, but is more 'user-friendly'. See 'strict_value_holder'
// class if you need strict type support.

// Supports trivial integral, floating point and string types in 'strict'
// mode and also allows the storage to hold arbitrary values using
// 'boost::any' storage. Note that due to implementation details, 
// values of specific (non-default) types can't be serialized
// (we skip them / replace with dummy objects).
class value_holder {
   // Main value holder type, which has support for common types
   // and can use the internal visitor for casting.
   typedef boost::variant Holder;
public:
   // Models 'DefaultConstructible', default constructor
   // leaves the storage uninitialized.
   value_holder() : using_any_holder_(false) { }

   template  value_holder(const T& value, typename boost::enable_if<
      typename boost::mpl::contains >::type* = 0)
      : holder_(value), using_any_holder_(false) { }

   // Custom constructing routine for string-like types.
   explicit value_holder(const char* value)
      : holder_(std::string(value)), using_any_holder_(false) { }

   // Custom constructing routine for string-like types.
   explicit value_holder(const wchar_t* value)
      : holder_(std::wstring(value)), using_any_holder_(false) { }

   // Custom constructing routine for non-standard types.
   template  value_holder(const T& value, typename boost::disable_if<
      typename boost::mpl::contains >::type* = 0)
      : any_holder_(value), using_any_holder_(true) { }

   // Retrieves held value with possible additional type casts.
   // Note that this method won't even compile for unsupported
   // types.
   template 
   typename boost::enable_if, T>::type as() const {
      // Apply internal casting visitor.
      return boost::apply_visitor(type_casting_visitor(), holder_);
   }

   // Attempts to retrieve non-standard type from internal 'boost::any'-based
   // storage. Throws 'boost::bad_any_cast' on errors.
   template 
   typename boost::disable_if, T>::type as() const {
      // Apply internal 'boost::any_cast' routine.
      return boost::any_cast(any_holder_);
   }

   // Generic holder swapping (required by 'boost::property_tree'
   // specification).
   void swap(value_holder& value) {
      holder_.swap(value.holder_);
      any_holder_.swap(value.any_holder_);
      std::swap(using_any_holder_, value.using_any_holder_);
   }

   // Check if current holder is empty (required by 
   // 'boost::property_tree' specification).
   bool empty() const {
      // Dispatch emptiness check based on currently
      // used value holder.
      if (using_any_holder_) return any_holder_.empty();

      return holder_.empty();
   }

private:
   // Internal functional object used to retrieve common types and perform
   // appropriate casting.
   template 
   struct type_casting_visitor : boost::static_visitor {
      // Handles every possible arithmetic type with
      // 'boost::numeric_cast'.
      template  T operator()(const Y& value) const {
         return boost::numeric_cast(value);
      }

      // Template specialization for 'std::string' -> type casting
      template <> inline T operator()(const std::string& value) const {
         return boost::lexical_cast(value);
      }

      // Template specialization for 'std::wstring' -> type casting
      template <> inline T operator()(const std::wstring& value) const {
         return boost::lexical_cast(value);
      }
   };

   // Template specialization for type -> 'std::string' casting.
   template <> 
   struct type_casting_visitor : boost::static_visitor {
      template  std::string operator()(const Y& value) const {
         return boost::lexical_cast(value);
      }
   };

   // Template specialization for type -> 'std::wstring' casting.
   template <> 
   struct type_casting_visitor : boost::static_visitor {
      template  std::wstring operator()(const Y& value) const {
         return boost::lexical_cast(value);
      }
   };

public:
   // Custom serialization implementation.
   template  void serialize(Archive& ar, const unsigned int) {

      using namespace ::boost;

      ar & serialization::make_nvp("using_any_holder", using_any_holder_);

      // Serialize only what we can - if the value shares one
      // of the predefined types, use the default 'boost::variant'
      // serialization routine.
      if (!using_any_holder_) ar & serialization::make_nvp("holder", holder_);
   }

private:
   // Main value holder instance.
   Holder holder_;

   // Alternative value holder, which is used to store 
   // objects and data that differ from supported by main holder.

   // These can actually be treated as temporary objects, because
   // due to language / implementation limitations, they aren't
   // serialized / deserialized (it's impossible to combine benefits
   // from 'generics' while still having the strict typing - which
   // is a requirement for our serialization routines).
   boost::any any_holder_;

   // Indicates if the current value is actually using a 'boost::any'-
   // based value holder (non-common value).
   bool using_any_holder_;
};

// Strict variation of generic value holder, which
// does not support arbitrary types and will
// throw if types do not match exactly.
class strict_value_holder : public value_holder {
   // Main value holder type, which has support for common types.
   typedef boost::variant Holder;
public:
   // Models 'DefaultConstructible'.
   strict_value_holder() { }

   // Custom constructors for any of the available basic cases (which are
   // supported by initial 'boost::variant' placeholder).
   template  strict_value_holder(const T& value, typename boost::enable_if<
      typename boost::mpl::contains >::type* = 0)
      : holder_(value) { }

   // Custom constructing routine for string-like types.
   explicit strict_value_holder(const char* value)
      : holder_(std::string(value)) { }

   // Custom constructing routine for string-like types.
   explicit strict_value_holder(const wchar_t* value)
      : holder_(std::wstring(value)) { }

   // Retrieves held value without any type casts. Throws 'boost::bad_get' if
   // casting was unsuccessful.
   template 
   typename boost::enable_if, T>::type as() const {
      return boost::get(holder_);
   }

   // Generic holder swapping (required by 'boost::property_tree'
   // specification).
   void swap(strict_value_holder& value) { holder_.swap(value.holder_); }

   // Check if current holder is empty (required by 
   // 'boost::property_tree' specification).
   bool empty() const { return holder_.empty(); }

public:
   // Custom serialization implementation.
   template  void serialize(Archive& ar, const unsigned int) {

      using namespace ::boost;

      // Serialize the value holder.
      ar & serialization::make_nvp("holder", holder_);
   }

private:
   // Main value holder instance.
   Holder holder_;
};

// Custom holder-based translator implementation.
template  struct value_holder_translator {
   // Type definitions required by the
   // 'Translator' concept.
   typedef T internal_type;
   typedef Y external_type;

   boost::optional get_value(const T& value_holder) const {
      // Attempt to use casting routine
      // specific for template type.
      return value_holder.as();
   }

   boost::optional put_value(const Y& value) const {
      // Construct appropriate value holder for specified
      // value (doesn't throw).
      return T(value);
   }
};

// Set custom translator for internal 'value_holder' type.
template  struct translator_between {
   typedef value_holder_translator type;
};

// Set custom translator for internal 'strict_value_holder' type.
template  struct translator_between {
   typedef value_holder_translator type;
};

} // namespace property_tree

// Type definition for custom holder-based properties collection. Note that
// for convenience, properties collection is brought to '::boost' namespace.

// This kind of properties allows arbitrary parameter types and, therefore,
// will substitute unsupported parameter types with dummies when writing. Also
// note, that if the parameter structure is filled from some xml-like file,
// you will lose the performance of custom value holder, because in this
// case parameters are stored as strings (that obviously brings a huge
// casting overhead to 'get' operations).

// If you want to save the benefits of type checking and remove type casting
// overhead, use serialization instead of raw xml writing / reading.

typedef property_tree::basic_ptree<
   std::string, property_tree::value_holder> properties;

// This variation of properties structure supports
// wide character keys.
typedef property_tree::basic_ptree<
   std::wstring, property_tree::value_holder> wproperties;

// Bring property-specific path declaration into the '::boost'
// namespace.
using property_tree::path;
using property_tree::wpath;

// Enable custom tree-like format parsers.
using property_tree::xml_parser::write_xml;
using property_tree::xml_parser::read_xml;
using property_tree::json_parser::write_json;
using property_tree::json_parser::read_json;
using property_tree::ini_parser::write_ini;
using property_tree::ini_parser::read_ini;
using property_tree::info_parser::write_info;
using property_tree::info_parser::read_info;

} // namespace boost

    


Ответы

Ответ 1



Проблема в том, что сама идея класса QSettings бесполезна. The QSettings class provides persistent platform-independent application settings. Что в реальности мешает вашей Windows программе выкачать "application settings" стиле Linux из текстового файла (ini, conf, XML, json, e.t.c), а не из реестра, при этом используется более распространенная библиотека iostream.h, а не специфичная Qt библиотека QSettings.h? P.S. При таком подходе обеспечивается поддержка любой платформы, в которой есть файлова система, и вообще отпадает необходимость в обобщенном программировании и шаблонах, из которых на 95% состоит ваш код.