Текирова, Кемер, Турция

Писать или нет?, — думал я. По дороге в аэропорт провожающий гид «обрадовал», что рейс будет «Анталия-Махачкала», а не «Анталия-Петербург». И это ещё везение — кому-то до нас достался Новосибирск или Самара. «Точно писать!» — это сигнал свыше. Махачкала — это как будто Россия, но Дагестан, а достаточна ли у меня длина рукавов и штанин для посещения данной местности — я не очень уверен.

Итак, мы купили относительно недорогой тур в трехзвездочный отель Турции на семь дней. Что сказать, отель так себе, но радушие хозяев и местная живность в лице немецкой овчарки Беллы, рыжего кота, желтоглазой кошки и четырёх озорных котят подкупают, поэтому о минусах отеля писать не буду. Мы славно ели, крепко спали, подкармливали зверушек на завтрак и ужин. В отеле у нас всё прошло хорошо.

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

Жили мы в городке Текирова, почти на краю Кемерской губернии и на экскурсии нас забирали первыми и возвращали последними (иногда наоборот). Из-за этой особенности водителям было лениво отвозить нас так далеко от Анталии, и они дважды пытались «сбагрить» нас товарищам, которым было по пути. Один раз нам удалось от такой пересадки отбиться, в другой — нет.

Ощущения, когда ты последний пассажир ночью в автобусе с незнакомыми турецкими мужиками — незабываемое. Особенно они обостряются после рассказа о беженцах-наркоманах из Сирии которые могут и ножом пырнуть, плюс ИГИЛ — всё ещё действующая организация, до которой ехать километров пятьсот.

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

Итак, насчёт экскурсий раздумья такие: больше их покупать не будем, арендуем машину/мопед и прокатимся по Кемерскому краю самостоятельно. Кстати, вот вам полезный совет: открываете сайт с названием местного тур оператора (Marş Travel, Şereşe Travel, Ginza Travel и т.д.) и требуете, чтобы экскурсию вам продали по интернет цене, иначе продаван называет цену, какая придёт в его светлую темноволосую голову. Мы были на экскурсии, где цена варьировалась от 25 до 40 баксов за человека, причём состав и условия экскурсий был одинаковыми. Мы купили несколько поездок: хамам, Демре-Мира-Кекова, бухта Порто Дженевиз, развалины города Перге, теперь по порядку о каждой поездке без прикрас.

Хамам

Отвозят на маршрутке в общественный хамам. Впервые был в очереди в бане, в целом хамам — это хорошо и обязательно к посещению а начале отпуска, только не этот конкретно, лучше бы взяли хамам в соседнем отеле. На обратном пути водитель пытался нас пересадить в другую маршрутку, но там не хватило сидячих мест, так что удалось отбиться с небольшим скандалом. Цена двадцать баксов с человека. С нами ехали те, кто взял за 18 баксов и за 25, так что делайте выводы.

Демре Мира Кекова

Посещение трёх городов и церкви Николая Чудотворца, также известного как Санта Клаус.

Оказалось, Санта не живёт с женой на Северном Полюсе, а давно умер, и власти скрывают! Перед покупкой экскурсии вам льют в уши, что вы поплывёте на судне со стеклянным дном обозревать руины древних городов, по факту получаются два грязных окошка, в которые ничего не видно кроме зелёной воды. Глумиться про религию не буду (это не одобряют жена и УК РФ), но давно я не видел такого коммерциализированного подхода к религии, особенно в сфере продаж реквизита. Из хорошего — посещение церкви Святого Николая Чудотворца. Монументальное и очень древнее строение. Удивительно, как в начале нашей эры смогли возвести подобное сооружение.

Бухта Порто Дженевиз

Прогулка на кораблике с купанием по трём бухтам: два раза по часу и один на полчаса. Старт корабля из Адрасана. Рекомендую вместо этого купить экскурсию на турецкие Мальдивы — Сулуада. Там вода чище, виды красивее и в целом лучше. Экскурсия запомнилась знакомством с байкерами из Нижнего Новгорода, которые своим ходом через Грузию за шесть дней доехали до Кемера. Из неприятного — тётка в маршрутке, которая заняла чужое место и внезапно забыла русский язык. Так мы и познакомились с байкерами.

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

Перге

Четыре события по цене одного и пятое в подарок. Сначала везут в парк к верхнему Дюденскому водопаду. Потом часик гуляем по развалинам древнего города Перге. Потом — ужин вкуснейшими котлетками-кюфте, много-много пахлавы, турецкий чаёк. Далее отвозят в «Land of legends» с шоу фонтанов, красивым замком Nickelodeon и элитными магазинами. В завершение программы — вишенка на торте: пересадка посреди ночи из комфортного автобуса в ссаную маршрутку с двумя неизвестными турками, потому что водителю лениво далеко ехать, зато бакшиш брать не лениво.

Небольшая новость

Всем привет! Всякому программисту положено иметь pet-проект, который он холит, лелеет и делает в свободное время.

Когда я задумался о таком, оказалось, что мой pet-проект — это собственно сайт blog2k.ru, для которого я написал свою тему для WordPress и поддерживаю собственный WordPress плагин со всякими полезными плюшками.

Буквально сегодня выложил в открытый доступ на github исходники своих наработок:

  • тема для WordPress этого сайта: b2k-theme
  • плагин с полезным функционалом: b2k-tools
  • виджет, который при добавлении в область title sidebar, показывает список постов c текущей категорией или тегом: wp-index-widget
  • плагин для виджета отображения погоды: yowindow-widget

Работу плагина wp-index-widget можно увидеть, например, здесь.

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

Коммиты и форки всячески приветствуются и не осуждаются. Пул реквесты также, возможно, будут приняты. Enjoy!

Пул потоков с запуском функций с переменным количеством параметров

Всем привет! Внезапно оказался без определённого места работы и в свободное время мучаю ИИ по поводу возможных задач и вопросов на собеседованиях по C++, и некоторые задачи делаю самостоятельно.

Недавно я попросил ИИ сделать пул потоков. Я его написал заранее, но захотелось проверить, где я мог ошибиться.

ИИ написал нерабочий код, но с интересной идеей: запускать вместо стандартного std::function<void()> функции с параметрами с помощью std::packaged_task и variadic templates. Меня этот так вдохновило, что я решил таки сделать этот код рабочим. И сделал!

Кстати, мой код не нравится ИИ — он предлагает его переделать на std::condition_variable и, возможно, он прав, а я написал ерунду, таков путь!

Мой вариант на C++:

#include <vector>
#include <atomic>
#include <thread>
#include <mutex>
#include <functional>
#include <deque>
#include <optional>
#include <future>
#include <iostream>

class ThreadPool {
public:
    /// По умолчанию количество потоков равно количеству аппаратных потоков
    ThreadPool(size_t numThreads=std::jthread::hardware_concurrency()) {
        m_threads.reserve(numThreads);
        for (size_t i = 0; i < numThreads; ++i) {
            m_threads.emplace_back([this](){ runLoop(); });
        }
    }

    // Принимаем на входе функцию и её аргументы, на выходе автоматически
    // оборачиваем результат работы функции в std::future
    template<typename... Args>
    auto runTaskAsync(auto f, Args&&...args) {
        // Выясняем возращаемый тип у функции
        using return_type = std::invoke_result_t<decltype(f), Args...>;

        // Заворачиваем функцию с переменным количеством аргументов в
        // std::packaged_task, у которого можно получить std::future<return_type>
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<decltype(f)>(f), std::forward<Args>(args)...)
            );

        // Получаем std::future
        auto res = task->get_future();

        if (m_Running) {
            std::unique_lock lock(m_guard);
            // Добавляем задачу в очередь на выполнение
            m_tasks.push_back([task](){(*task)();});

            // Уведомляем ожидающие потоки
            m_hasTask = true;
            // Может тут надо использовать notify_one (?)
            m_hasTask.notify_all();
        }

        return res;
    }

    // Вызываем функцию синхронно и возвращаем результат
    template<typename...Args>
    auto runTaskSync(auto f, Args &&...args) {
        return runTaskAsync(f, std::forward<Args>(args)...).get();
    }

    ~ThreadPool()
    {
        // Устанавливаем флаг остановки потоков
        m_Running = false;
        // Очищаем очередь задач
        {
            std::unique_lock lock(m_guard);
            m_tasks.clear();
        }
        // Обманом :-) выводим потоки из режима ожидания
        m_hasTask = true;
        m_hasTask.notify_all();

        // Для каждого потока
        for (auto &thread : m_threads) {
            if (thread.joinable()) {
                // Дожидаемся завершения работы
                thread.join();
            }
        }
    }
private:
    using Task = std::function<void()>;

    void runLoop() {
        while (m_Running) {
            // Ждём задачу
            m_hasTask.wait(false);

            std::optional<Task> task;

            // Забираем задачу из начала списка
            {
                std::unique_lock lock(m_guard);
                if (!m_tasks.empty()) {
                    task = m_tasks.front();
                    m_tasks.pop_front();
                }
            }

            // Задача есть, исполняем
            if (task) {
                (*task)();
            }
            else {
                // Отдаём процессорное время другим потокам
                std::this_thread::yield();
            }
        }
    }

    // Вектор потоков
    std::vector<std::thread> m_threads;
    // Атомарный флаг остановки потоков
    std::atomic_bool m_Running{ true };
    // Атомарный флаг наличия задачи
    std::atomic_bool m_hasTask{ false };
    // Очередь задач
    std::deque<Task> m_tasks;
    // Мьютекс для защиты очереди задач
    std::mutex m_guard;
};


int main()
{
    // Создаём пул потоков
    ThreadPool mgr;

    // Первая задача на две секунды, которая возвращает строку
    auto func = [](const std::string &s) {
        std::cout << "Long task1 begin..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(2));
        std::cout << "Long task1 end..." << std::endl;
        return s;
    };

    // Вторая задача на пять секунд, которая ничего не возвращает
    auto func2 = []() {
        std::cout << "Long task2 begin..." << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(5));
        std::cout << "Long task2 end..." << std::endl;
    };

    // Запускаем первую задачу с параметром
    auto result1 = mgr.runTaskAsync(func, "task1 result");
    // Запускаем вторую задачу без параметров
    auto result2 = mgr.runTaskAsync(func2);

    // Получаем результат выполнения первой задачи и выводим в консоль
    std::cout << "Task1 result = " << result1.get() << std::endl;
    // Ждём вторую задачу
    std::cout << "Wait task2 result" << std::endl;
    result2.wait();
}

Исходник от ИИ для сравнения:

#include <vector>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>
#include <memory>
#include <stdexcept>

class ThreadPool {
public:
    // Конструктор, создающий указанное количество потоков
    ThreadPool(size_t threads) : stop(false) {
        for(size_t i = 0; i < threads; ++i) {
            workers.emplace_back([this] {
                for(;;) {
                    std::function<void()> task;
                    
                    {
                        std::unique_lock<std::mutex> lock(this->queue_mutex);
                        this->condition.wait(lock, 
                            [this]{ return this->stop || !this->tasks.empty(); });
                        if(this->stop && this->tasks.empty())
                            return;
                        task = std::move(this->tasks.front());
                        this->tasks.pop();
                    }
                    
                    task();
                }
            });
        }
    }
    
    // Добавление задачи в пул
    template<class F, class... Args>
    auto enqueue(F&& f, Args&&... args) 
        -> std::future<typename std::result_of<F(Args...)>::type> {
        using return_type = typename std::result_of<F(Args...)>::type;
        
        auto task = std::make_shared<std::packaged_task<return_type()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
            
        std::future<return_type> res = task->get_future();
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            
            // Не добавляем новые задачи после остановки пула
            if(stop)
                throw std::runtime_error("enqueue on stopped ThreadPool");
                
            tasks.emplace([task](){ (*task)(); });
        }
        condition.notify_one();
        return res;
    }
    
    // Деструктор, останавливающий все потоки
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(queue_mutex);
            stop = true;
        }
        condition.notify_all();
        for(std::thread &worker: workers)
            worker.join();
    }

private:
    std::vector<std::thread> workers;       // Рабочие потоки
    std::queue<std::function<void()>> tasks;// Очередь задач
    
    std::mutex queue_mutex;                 // Мьютекс для синхронизации доступа к очереди
    std::condition_variable condition;      // Условная переменная для уведомлений
    bool stop;                              // Флаг остановки пула
};

Буду признателен, если вы укажете на возможные ошибки и недочёты в моём коде.

Летний гастротур по России

Зеленоградск

Изначально мы прилетели в Калининград и взяли такси до Зеленоградска. Вдоль трассы полно зелени и полей, ехать 20 минут, такси стоит недорого относительно Петербурга.

Заселились в отель «Кранц», немецкое название Зеленоградска именно такое. Отель не понравился. Стоит в два раза дороже, чем должен. Единственный плюс отеля — его расположение недалеко от берега, остальное плохо — туалет и ванная одна на два номера, завтрак советский из полуфабрикатов, кофе растворимый, вешалок в номере нет, подниматься по крутой лестнице на третий этаж сложно с пустыми руками, с чемоданами вообще шею можно сломать, по уборке в номере возникли вопросы.

Но город, в отличие от отеля, оставил самые приятные впечатления, несмотря на сильный ветер и температуру в 12-16 градусов. Так получилось, что поездка была больше гастротуром, чем туризмом, не успевали испытывать голод. Перепробовали всё, привезли лишнего весу килограмм шесть на двоих.

Напробовался рыбы разных видов и способов приготовления. Тут тебе и строганина из пеламиды, и тартар из лосося, и шаверма из тунца. Так-то рыбу я не ем особо, а тут прям ушёл в отрыв.

После приезда, отдохнув от поездки, вечером посетили колесо обозрения «Глаз Балтики» и обозрели весь Зеленоградск сверху.

Церкви в Зеленоградске получаются из католических костёлов: добавляют костёлу золотой купол и получается цыганский дворец с элементами готики. Выглядит не к месту и чужеродно на мой непритязательный взгляд. Зачем так сделано — лично мне непонятно.

Церковь трансформер из хостела

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

Зеленоградск. Мурал с котами.

Есть музей кошек Мурариум в бывшей водонапорной башне. Сверху открывается хороший вид на весь Зеленоградск. Сам музей содержит коллекцию разнообразных фигурок кошек, поделок в виде кошек, открыток с кошками и так далее. Имеет смысл посетить хотя бы ради вида с последнего этажа.

Новостройки в Зеленоградске стараются делать в стиле а-ля фахверк. С высоты город выглядит приятно, и в нём хочется остаться жить. Цены на квартиры — питерские.

Посетили музей миниатюр, послушали историю про остров богачей (остров Канта), который разбомбили британские самолёты, и про замок в Калининграде, которые советские власти решили не восстанавливать, и сейчас там парковка.

Первый вечер мы провели в ресторане «Телеграф», где я впервые увидел живого сомелье и строганину из пеламиды. Живой сомелье посоветовала оранжевое вино, которое делает винодел Никита из Севастополя, а строганина — это замороженное сырое мясо рыбы. Как ни странно, мне понравилось.

Ресторан

Во второй вечер пошли в ресторан «Балт», где вкушали скумбрию с настойками. Скумбрия прекрасна, настойки — водка с вареньем. Рыбу съели, настойки не оценили и пошли в «Телеграф», где они выше всяких похвал. Вкусы настоек в «Телеграфе» разнообразные: от малины и марципана до настоек на грибах и бородинском хлебе. Настойки очень понравились, рекомендую. Будете в Зеленоградске, обязательно зайдите в «Телеграф» хотя бы за настойками, а в «Балт» за скумбрией.

Также мы посетили кафе «Гнездо», куда пёрлись по пляжу километра четыре под шквалистым ветром и ничего там толком не съели: скумбрия с костями, угорь с куском жира, картошка залита маслом. Блюда можно есть только вприкуску с Панкреатином. Жирно, невкусно и с костями.
Ещё вкушали добротные (в отличие от гостиницы) завтраки в кафе «Бранч на море». Совсем рядом ресторан «Огонёк», — тоже посетили и остались довольны.

На Куршскую косу, к сожалению, не попали. Ну ничего, будет повод приехать.

Если у вас сложилось впечатление, что мы бродили от стола к столу, преодолевая холод голод и ветер, то так оно и было. Зеленоградск в целом приятный городок, мы бы вернулись туда поесть ещё раз. Следующим местом по плану был Светлогорск, куда мы отправились на каршеринге CityRent.

Светлогорск

Оказалось, что каршеринг берёт дополнительный оброк за переезд внутри Калининградской области между городками, ценник получился в два раза дороже такси, поэтому CityRent мы использовали первый и последний раз в жизни. Копеечку заработали, клиента потеряли — молодцы, типичный российский бизнес, как он есть.

Светлогорск. Водонапорная башня

Гостиница наша располагалась в посёлке Отрадное в двух километрах от Светлогорска. В Светлогорске делать нечего, в Отрадном тем более. В этой части путешествия не понравилось абсолютно ничего. Вдоль моря не погулять, все значимые места закрыты на перестройку или реставрацию. После семи вечера всё закрывается, смотреть не на что. Потерпев Светлогорск три дня, отправились в Калининград.

Калининград

Город Калининград и область оставили впечатление, будто территорию захватили, а как содержать — не знают, поэтому преследовало чувство упадка, и грязные хрущевки это чувство только усиливали. Что интересно: трамваи в Калининграде имеют более узкую колею, чем в Петербурге, и поэтому выглядят, как детская железная дорога. Посетили мастер-класс по изготовлению фигурок из марципана, с последующей их окраской, обзорную экскурсию по рекам и каналам. Затем побывали на подводной лодке, внутри тесно, но очень интересно. Понравился собор на острове Канта и район, где расположен уютный бар «У вас горизонт завален», где развлекались до ночи и меняли оливки на клубнику. Спустя два дня (как голова перестала болеть) отправились в Казань.

Казань

В Казани заселились в отель Innjoy, что находится на главной туристической улице Баумана, что гарантировало минимальный путь к достопримечательностям, но при этом народ гулял и шумел на улице до утра. Казань выглядит как мусульманская смесь Санкт-Петербурга и Москвы. Посетили мечеть «Кул Шариф» с музеем внутри. Ислам в Казани, можно сказать, дружелюбный, с человеческим лицом — такой, каким он и должен быть, а не тот, который демонстративно тащат с собой приезжие из аулов. В музее почитали жалобу на татар от православных исследователей на старославянском. Читали сквозь смех и слёзы.

Ермоген Митрополит их призывал в соборную церковь Пречистыя Богородицы и поучал их от божественного писания и наказывал как подобает крестьянам жить и они ученья не принимают и от Татарских обычаев не отстанут

И умерших в церкви хоронить не носят, кладут по старым своим Татарским кладбищам

И с женками , и с девками с некрещенными живут мимо своих жен

Мечеть

Кормили в отеле хорошо. На улице Баумана забрели в заведение «Рёбра на огне», которое нас совсем не впечатлило: концепция «есть руками без вилок» не понравилась, мясо жестковато, первую порцию свиных рёбер вообще принесли недожаренной. Их рёбра проигрывают Holy Ribs по всем параметрам.

Пермь

Саму Пермь толком не смотрели, прогулялись от вокзала Пермь-1 через набережную мимо особняков купцов Жирновых, зарулили в блинную, схватили по-быстрому еды и поехали на вокзал Пермь-2. Жили у родственников за городом, проживание — пять звезд, всё включено, даже laundry hotel service. «Шанежки» были такие большие, что назывались гордо «Шаньги», и я даже сразу их не распознал. Думаю, если бы тётя взялась делать «посикуньчики», они бы звались «посикуны» из-за размера.

Итог

Подведу итог: Казань и Зеленоградск хочется посетить ещё раз, когда-нибудь. Цены практически везде питерские, а зарплаты — нет. Как выживает местное население, мне неведомо. Только такси местами дешевле, но не намного. Между городами передвигались на самолётах, из Казани до Перми — поездом.

А по возвращению в Питер, мы двинули в Кингисепп, но это уже другая история, и вы её не услышите.

Как найти сигнатуру Java/Kotlin метода для работы через JNI

Для того, чтобы получить ссылку на Java метод, нужно знать его сигнатуру. Есть два стула способа:

1. Подобрать сигнатуру по таблице:

Сигнатура Java тип
V void
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L fully-qualified-class ; fully-qualified-class
[ type type[]
( arg-types ) ret-type method type

2. Получить сигнатуру через javap

%JAVA_HOME%/bin/javap -classpath "your.jar" -s com.myproject.YourClass

Пример:

Для метода:

public static void runTask(Runnable runnable) {
    runnable.run();
}

Сигнатура будет: runTask(Ljava/lang/Runnable;)V — принимает объект класса java.lang.Runnable и возвращает void.
Для встроенных классов (например android.os.Handler) ищите .jar в Android_SDK.

Совет:

Для С++ удобнее использовать библиотеку jnipp вместо jni.h

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

Блог Евгения Жирнова