LINUX.ORG.RU

Сообщения KivApple

 

Структура данных

Форум — Development

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

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

Какая структура данных лучше всего для этого подходит?

 , ,

KivApple
()

CMake + vcpkg + EMSDK

Форум — Development

Допустим, есть простенький C++ проект:

CMakeLists.txt

cmake_minimum_required(VERSION 3.14)

include(FetchContent)
FetchContent_Declare(vcpkg GIT_REPOSITORY https://github.com/microsoft/vcpkg/ GIT_TAG 2022.09.27)
FetchContent_MakeAvailable(vcpkg)
set(CMAKE_TOOLCHAIN_FILE "${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake" CACHE FILEPATH "")

project(MyProject)

set(CMAKE_CXX_STANDARD 20)

find_package(spdlog CONFIG REQUIRED)

add_executable(${CMAKE_PROJECT_NAME} main.cpp)
target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE spdlog::spdlog)

vcpkg.json

{
        "name": "main",
        "version-string": "latest",
        "dependencies": [
                "spdlog"
        ]
}

main.cpp

#include <spdlog/spdlog.h>

int main() {
        spdlog::info("Hello world!");
        return 0;
}

Пытаемся его собирать нативно:

$ cmake .. && make && ./MyProject
...
[100%] Built target MyProject
[2022-10-23 14:41:07.376] [info] Hello world!

Всё работает.

Теперь, допустим, мы его хотим собрать под WASM:

$ source "/home/kiv/emsdk/emsdk_env.sh"
...
$ cmake .. -DVCPKG_TARGET_TRIPLET=wasm32-emscripten && make
-- Running vcpkg install
-- Running vcpkg install - done
CMake Error at build-wasm/_deps/vcpkg-src/scripts/buildsystems/vcpkg.cmake:829 (_find_package):
  Could not find a configuration file for package "spdlog" that is compatible
  with requested version "".

  The following configuration files were considered but not accepted:

    /home/kiv/my_project/build-wasm/vcpkg_installed/wasm32-emscripten/share/spdlog/spdlogConfig.cmake, version: 1.10.0 (32bit)

Call Stack (most recent call first):
  CMakeLists.txt:12 (find_package)


-- Configuring incomplete, errors occurred!
See also "/home/kiv/my_project/build-wasm/CMakeFiles/CMakeOutput.log".

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

$ mkdir tmp && cd tmp
$ ar x ../vcpkg_installed/wasm32-emscripten/lib/libspdlog.a
$ file spdlog.cpp.o
spdlog.cpp.o: WebAssembly (wasm) binary module version 0x1 (MVP)

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

Возникает вопрос, как заставить CMake всё же использовать найденную библиотеку.

Пробуем указывать точную версию find_package(spdlog «1.10.0 (32bit)» CONFIG REQUIRED) - получаем ошибку недопустимого аргумента (вероятно, из-за пробела в версии). Если указывать же просто 1.10.0, то получаем старую ошибку и ничего не меняется.

Возникает вопрос, как find_package определяет совместимость библиотеки и как изменить его мнение в данной ситуации.

 , , vcpkg

KivApple
()

C++ modules и системы сборки

Форум — Development

Кто как собирает проекты с использованием C++ modules?

Компиляторы уже давно умеют их, например, в clang даже объявили deprecated опцию -fmodules-ts и поддержка модулей автоматически включается при активации стандарта C++20.

Однако, собирать руками проекты удовольствия мало. Хочется использовать какую-нибудь систему сборки. И вот тут возникает проблема. Самая популярная система сборки в мире C/C++ - CMake - поддерживает модули только для MSVC, что как бы не очень интересно (так как ограничивает одной платформой, к тому же clang/gcc по моим тестам обычно генерируют более оптимальный код, чем MSVC, даже под Windows). Какие есть альтернативы? Можно ли приобщиться к модулям не путём написания вручную Makefile или Bash-скриптов?

 , ,

KivApple
()

Rust и обёртка над блокировкой

Форум — Development

Допустим, у нас есть некая мутабельная структура данных (в данном случае это просто число, но это лишь для простоты примера), которую мы хотим шарить между потоками. При этом мы хотим сделать свою RAII обёртку над блокировкой этой сущности, чтобы предоставить пользователю определённый интерфейс её модификации (а также отслеживать разблокировку и т. п.).

Получается как-то так (лайфтаймы у обёртки от балды, потому что я не знаю как это правильно написать):

use std::sync::{Arc, Mutex, MutexGuard};

pub struct SharedData {
    data: Arc<Mutex<i32>>
}

impl SharedData {
    pub fn new(value: i32) -> Self {
        SharedData {
            data: Arc::new(Mutex::new(value))
        }
    }

    pub fn lock(&self) -> LockedSharedData<'_> {
        LockedSharedData::new(self.data.clone())
    }
}

pub struct LockedSharedData<'a> {
    _data: Arc<Mutex<i32>>,
    guard: MutexGuard<'a, i32>
}

impl<'a> LockedSharedData<'a> {
    fn new(data: Arc<Mutex<i32>>) -> Self {
        LockedSharedData {
            guard: data.lock().unwrap(),
            _data: data
        }
    }

    pub fn get(&self) -> i32 {
        *self.guard
    }

    pub fn inc(&mut self) {
        *self.guard += 1;
    }
}

Оно ожидаемо не компилируется - https://rust.godbolt.org/z/4rEe3fWxK :

error[E0515]: cannot return value referencing function parameter `data`
  --> <source>:26:9
   |
26 | /         LockedSharedData {
27 | |             guard: data.lock().unwrap(),
   | |                    ----------- `data` is borrowed here
28 | |             _data: data
29 | |         }
   | |_________^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `data` because it is borrowed
  --> <source>:28:20
   |
24 |   impl<'a> LockedSharedData<'a> {
   |        -- lifetime `'a` defined here
25 |       fn new(data: Arc<Mutex<i32>>) -> Self {
26 | /         LockedSharedData {
27 | |             guard: data.lock().unwrap(),
   | |                    ----------- borrow of `data` occurs here
28 | |             _data: data
   | |                    ^^^^ move out of `data` occurs here
29 | |         }
   | |_________- returning this value requires that `data` is borrowed for `'a`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
Compiler returned: 1

Собственно, вопрос как это переписать правильно, чтобы Rust устраивали лайфтаймы, но при этом сохранить инкапсуляцию заблокированного значения в структуру блокировки.

 ,

KivApple
()

Небольшой бенчмарк Rust vs C++

Форум — Talks

Я тут вам покушать принёс.

Rust: https://github.com/KivApple/rust_benchmark

C++: https://github.com/KivApple/cxx_benchmark

Реализован алгоритм построения icosphere. Этот алгоритм запускается 10000 раз и замеряется среднее время исполнения. Алгоритм реализован практически дословно на обоих языках. Для небольшой математики используется glm для C++ и glam для Rust. Также алгоритм использует стандартные контейнеры HashMap и Vec для Rust и std::unordered_map и std::vector для C++.

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

Rust: 211 us
MSVC: 606 us
GCC: 497 us
Clang: 575 us
Rust: 211 us
MSVC: 598 us
GCC: 496 us
Clang: 587 us

Тестовое железо: Core i7-1165G7, 64 GB RAM, Windows 11 (мне быстрее всего было это запустить на Windows машине, плюс так получилась возможность забенчить ещё и MSVC для полноты картины, так же надо учесть, что в программе нет ни I/O, ни многопоточности, так что едва ли ОС будет влиять на производительность, а если и будет, то опять же оба языка в равных условиях).

Все проекты собраны в Release режиме (cargo build --release, cmake -DCMAKE_BUILD_TYPE=Release) под 64 бита.

Критика бенчмарка принимается (я понимаю, что это один частный use-case, но в рамках него можете указать на какие-то неточности замеров или несоответствия алгоритмов для разных языков). Также можете попробовать запустить на своих машинах со своими компиляторами (код достаточно тривиальный для понимания).

UPD: Версии компиляторов:

> rustc --version
rustc 1.64.0 (a55dd71d5 2022-09-19)
> clang --version
clang version 15.0.2
Target: x86_64-pc-windows-msvc
Thread model: posix
> cl
Microsoft (R) C/C++ Optimizing Compiler Version 19.32.31329 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.
> gcc --version
gcc (GCC) 11.3.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 , , ,

KivApple
()

Rust: преобразования указателя на трейт в конкретный тип

Форум — Development

Хочу хранить мапу с экземплярами реализующими трейт. При этом на месте одного ключа я уверен, что будет всегда лежать экземпляр одной и той же реализации (в данном примере в качестве ключа выступает помимо прочего TypeId). С хранием проблем не возникает, возникает проблема с извлечением элементов.

use std::collections::HashMap;
use std::any::TypeId;
use std::rc::Rc;

trait MyTrait {
    fn new() -> Self where Self: Sized;

    // Тут ещё какие-нибудь методы
}

struct MyStruct {
    items: HashMap<(i32, TypeId), Rc<dyn MyTrait>>
}

impl MyStruct {
    fn get<T: MyTrait + 'static>(&mut self, key: i32) -> Rc<T> {
        if let Some(item) = self.items.get(&(key, TypeId::of::<T>())) {
            item.clone() as Rc<T>
        } else {
            let item = Rc::new(T::new());
            self.items.insert((key, TypeId::of::<T>()), item.clone());
            item
        }
    }
}

Как правильно конвертировать элемент при его извлечении в тип T?

 

KivApple
()

std::unordered_map + emplace + non-movable тип

Форум — Development

Допустим, мы хотим хранить в качестве значений в unordered_map неперемещаемый тип. Например, вот так:

#include <mutex>
#include <unordered_map>

struct MyStruct {
    std::mutex mutex;
    int data = 0;
};

void f(std::unordered_map<int, MyStruct> &map, int key) {
    map.emplace(key, {});
}

Получаем ошибку компиляции: https://godbolt.org/z/vaTc5rxfP

У std::mutex нет ни конструктора копирования, ни конструктора перемещения. Однако, такие типы допустимо хранить в качестве значений в данном типе контейнера, потому что под капотом происходит динамическое выделение памяти и контейнер гарантирует, что единственная операция инвалидирующая ссылки и указатели на элементы - erase. Значит за всё время жизни элемента его никто никуда не переместит.

Проблема в том, как сделать emplace. Если бы у MyStruct был конструктор с аргументами - всё было бы просто. А у MyStruct нет конструктора, либо можно добавить, но по логике программы имеет смысл лишь конструктор без аргументов. Однако передать пустой список аргументов в emplace я не могу.

 

KivApple
()

Посоветуйте структуру данных

Форум — Development

Хочу структуру данных удовлетворяющую следующим условиям:

- Хранит в себе пары ключ-значение. Ключи - числа от 0 до 4095. Значения - uint32_t, но в целом вряд ли принципиально для алгоритма.

- Элементы отсортированы по возрастанию ключа - имея в каком-то виде указатель на элемент можно со сложностью O(1) получить указатель на следующий или на предыдущий (то есть возможна дешёвая итерация в обе стороны, полный обход списка в порядке сортировки и обратном имеет сложность O(N) - просто выполнить O(1) для каждого элемента) в порядке сортировки ключей.

- Существует операция поиска элемента по ключу со сложностью не более O(logN). При этом если элемента с таким ключом нет, должен находиться элемент с максимальным ключом меньше запрошенного. Функция поиска возвращает в каком-то виде указатель на элемент, который позволяет не только узнать его значение, но и выполнять итерирование, а также вставку и удаление элементов перед или после него.

- Операции добавления и удаления элементов - дешёвые, но выполняются значительно реже итерирования. Сложность не более O(logN).

- Эффективность по памяти. Требуется добавлять необходимый минимум служебной информации к элементам (так как сами элементы всего лишь 6 байт). Ну и само собой нельзя взять и выделить память сразу под 4096 возможных элементов, когда в реальности может лежать лишь пара сотен.

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

- Здорово если бы добавление-удаление элементов не инвалидировало указатели на не затронутые элементы, но это опциональное требование.

Как я понимаю, разумно использовать какой-нибудь std::vector, в который запихнуть элементы с обвязкой, а указатели между элементами (для построения какого-нибудь двоичного дерева) делать в виде 16-битных индексов (ведь максимальное число уникальных ключей всего 4096). Вопрос в том какую структуру данных построить поверх вектора.

 ,

KivApple
()

PF2: Метрики шрифта

Форум — Development

Пытаюсь сделать самодельный рендеринг текста с помощью PF2 шрифтов и OpenGL. В качестве шрифта использую Droid Sans. Для начала рендерю его в «моноширинном» режиме (поддержку переменной ширины глифа добавлю потом) - просто создаю текстуру, где каждому глифу отводится область maxCharWidth * maxCharHeight, а при рендеринге соответственно на каждую плоскость соответствующую очередному символу натягиваю текстуру по нужным координатам. Проблема в том, что глифы в шрифте могут иметь размеры меньше максимальных и их в этом случае надо правильно позиционировать внутри их места, иначе получается вот так (строчные буквы ниже заглавных и попадают не туда):

https://freeimage.host/i/iI0Yxt

Алгоритм помещения очередного глифа в текстуру шрифта выглядит так:

struct PF2CharHeader {
	uint16_t width;
	uint16_t height;
	int16_t xOffset;
	int16_t yOffset;
	int16_t deviceWidth;
};

...

class PF2FontLoader {
	...
	int m_pointSize;
	int m_maxCharWidth;
	int m_maxCharHeight;
	int m_ascent;
	int m_descent;
	...
}

...

void PF2FontLoader::parseCharBitmap(int index, const PF2CharHeader &header) {
	int x0 = (index % m_colCount) * m_maxCharWidth; //+ header.xOffset;
	int y0 = (index / m_colCount) * m_maxCharHeight; //+ header.yOffset;
	auto base = m_deserializer.adapter().currentReadPos();
	for (int y = 0; y < header.height; y++) {
		int j = (y0 + y) * m_textureWidth + x0;
		for (int x = 0; x < header.width; x++) {
			int i = y * header.width + x;
			uint8_t byte = m_data[base + i / 8];
			if ((byte & (1 << (7 - i % 8))) != 0) {
				m_textureData[j + x] = {255, 255, 255, 255};
			}
		}
	}
}

Также у меня есть параметры m_pointSize, m_ascent и m_descent извлеченные из заголовка шрифта (см. полное описание формата - http://grub.gibibit.com/New_font_format).

По идее ими надо как-то воспользоваться, чтобы вычислить baseline и разместить глиф, когда его размер меньше максимального, однако использовать xOffset и yOffset не помогает. Во-первых, у «e» и «H» одинаковый нулевой yOffset (хотя буквы имеют разную высоту глифа), во-вторых, тогда некоторые другие глифы выходят за границы своего места в текстуре.

Также вот параметры шрифта Droid Sans:

PF2 font "assets/fonts/DroidSans-32.pf2"
PF2 font name: Droid Sans Regular 32
PF2 font family: Droid Sans
PF2 font weight: normal
PF2 font slant: normal
PF2 font point size: 32
PF2 font max char width: 36
PF2 font max char height: 36
PF2 font ascent: 34
PF2 font descent: 9
PF2 font character index has 873 Unicode code point(s) (873 unique glyph(s))

 , , ,

KivApple
()

Статически создаваемые объекты

Форум — Development

Собственно, пример:

https://godbolt.org/z/YEPoddsss

Здесь речь идёт от std::initializer_list и о std::tuple.

Функции f и g принимают константную ссылку (на уровене ассемблера то же самое, что и указатель). Сами передаваемые объекты не предполагают динамического выделения памяти. То есть по факту они представляют одинаковый набор байт каждый вызов функции. Значит, с точки зрения самого алгоритма ничто не мешает создать объект в секции констант и передавать просто ссылку на него (ведь ссылка константная, а значит f и g не могут менять объект). Однако нет, каждый вызов f и g объекты конструируются на стеке. Хотя у tuple вообще есть constexpr конструктор.

Почему так? И способен ли современный C++ при каких-то условиях сконструировать временный объект во время компиляции и передавать ссылку на него в секции констант вместо конструирования на стеке? Если нет, то есть ли какие-то причины, почему эту фичу не реализуют?

UPD: Для std::tuple это работает как надо, если объявить аргумент отдельно как static переменную -https://godbolt.org/z/Y9q6Yh3q3. Для std::initializer_list не работает даже в этом случае (просто теперь он инициализируется не на стеке). Возможно ли создать аналог initializer_list (возможность передать в функцию переменное число аргументов с нормальной типобезопасностью и преобразованием типов при необходимости к правильному, а не как у темплейтов), который бы умел в статическое создание?

 ,

KivApple
()

C++: unordered_map с композитным ключом

Форум — Development

Допустим, я хочу, чтобы ключом в unordered_map выступала пара строки и числа.

Что-то вроде std::unordered_map<std::tuple<std::string, int>, MyStruct>. Хеширование легко можно реализовать следуя советам из https://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unorde....

А вот дальше возникает проблема. Чтобы сделать find требуется создать экземпляр ключа для поиска. Так как ключ включает в себя строку, произойдёт копирование (допустим, наша функция получает const std::string& и должна действовать), что не есть хорошо.

Какое представление строки не требует копирование при создании? Правильно, std::string_view. Давайте сделаем ключом std::tuple<std::string_view, int>. Теперь операция поиска будет очень дешёвой.

Однако, возникает проблема как вставить в коллекцию новый элемент. Ведь string_view не хранит строку, а лишь ссылку на неё. Логичным решением выглядит сохранить строку в значении. Изменим тип коллекции на std::unordered_map<std::tuple<std::string_view, int>, std::tuple<std::string, MyStruct>>.

Надо чтобы string_view из ключа ссылался на string из значения. Тогда можно будет искать по легковесному ключу std::tuple<std::string_view, int>, а копированием заниматься лишь при добавлении элемента.

Но... Но как сделать так, чтобы string_view ссылался куда надо в момент insert/emplace?

Или, возможно, я что-то делаю не так и использую не те структуры данных.

P.S.: tuple здесь для простоты, на его месте, разумеется может быть своя структура из двух полей с определенным оператором сравнения и хеширования.

 ,

KivApple
()

C++: функция с переменным числом аргументов

Форум — Development

Пытаюсь создать функцию с переменным числом аргументов типобезопасным способом с помощью шаблонов.

#include <iostream>
#include <concepts>

template<typename First, typename... Rest> 
requires std::is_convertible_v<First, const std::string&> && (std::is_convertible_v<Rest, const std::string&> && ...)
void f(First &&first, Rest&&... rest) {
    f(first);
    f(std::forward(rest)...);
}

template<>
void f(const std::string &first) {
    std::cout << first << std::endl;
}

int main() {
    f("foo", "bar");
}

Однако компилятор игнорирует специализацию f и жалуется об отсутствии версии f без аргументов.

Перемещено hobbit из general

 

KivApple
()

Кроссплатформанная графическая библитека

Форум — Development

Уточню, что речь идёт о трёхмерной графике.

Итак:

OpenGL - поддерживается буквально везде, включая веб (WebGL) и мобилки. Можно писать на пересекающемся подмножестве OpenGL 3.0 Core и OpenGL 3.0 ES и иметь современный код с шейдерами, который везде работает. Минусы: в теории имеет меньшую производительность, чем более новые библиотеки, но насколько я понимаю 95 процентов приложений не почувствуют разницу. Неясное будущее, как минимум на яблоках помечен как deprecated и может быть удалён в любой момент.

Vulkan - предлагается как дефолтная замена OpenGL. В теории должен давать более высокую производительность, а также имеет более определённое будущее. На яблоках работает через MoltenVK, на остальных платформах нативно. В вебе не работает.

Metal - замена OpenGL от Apple, очевидный минус - работает только на яблочном оборудовании. Vulkan выглядит привлекательнее.

DirectX - альтернативна OpenGL и Vulkan от Microsoft. Очевидный минус - работает только на винде и икс коробке. Vulkan выглядит привлекательнее.

Выходит что по факту выбор получается между OpenGL и Vulkan, так как остальные библиотеки это библиотеки одной платформы, причём вряд ли дающие каких-то киллер-фич по сравнению с этими двумя.

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

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

 , , ,

KivApple
()

Паттерн Builder в C++

Форум — Development

Является ли нижеприведенный код корректным кодом C++ и не содержит ли UB?

struct MyObject {
    int a;
    int b;
};

class MyObjectBuilder {
    int m_a = 0;
    int m_b = 0;
public:
    MyObjectBuilder &a(int value) {
        m_a = value;
        return *this;
    }
    MyObjectBuilder &b(int value) {
        m_b = value;
        return *this;
    }
    MyObject build() {
        return MyObject { m_a, m_b };
    }
};

MyObject f() {
    return MyObjectBuilder().a(10).b(20).build();
}

(меня напрягает возврат ссылки на this из метода объекта, когда сам объект является временным)

 , ,

KivApple
()

CMake и упаковка ресурсов приложения

Форум — Development

Моей программе требуются некоторые ресурсы во время работы. Хочется иметь возможность распространять программу в виде одного статически слинкованного файла (особенно актуально для офтопика), однако оставить возможность пользователю покопаться в ресурсах. Оптимальным решением выглядит упаковка ресурсов в zip архив и склеивание с исполняемым файлом приложения. Из-за особенностей структуры zip большинство архиваторов смогут открыть такой файл и даже работать с ним, несмотря на то, что это вообще то исполняемый файл.

Вопрос в том как его собирать не руками. Я использую CMake. У него есть команда cmake -E tar, позволяющая через add_custom_command/add_custom_target создать zip (и не только) архив. Вопрос в том, как его приклеить к исполняемому файлу. Если просто указать имя исполняемого файла в качестве выходного, то он полностью перезаписывается. Значит нужно создать промежуточный zip-файл и потом приклеить его к исполняемому после сборки. Однако, я пока не нашёл в CMake возможность для этого. Использовать утилиты типа dd плохая идея, так как тогда проект будет собираться только на онтопике, а на офтопике таких полезных утилит нет.

 ,

KivApple
()

Док Mac OS X

Форум — Desktop

Допустим, имеется Macbook Air на M1 и иногда даже за ним приходится работать.

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

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

Хотя на Linux и даже на Windows (когда там появились рабочие столы) эта фича давно есть и отлично работает.

Помогите...

 ,

KivApple
()

Python и миграции БД

Форум — Development

Какие есть альтернативы Alembic для организации миграции схемы БД в веб-приложениях на Python?

Я хочу миграции в виде SQL-скриптов (с опциональной возможностью иногда вызывать Python-код для сложных ситуаций), а не попыток писать недо-SQL на Python. Мне также не нужны миграции в обратную (down) сторону, только вперёд (эта функция может быть в библиотеке, но не надо заставлять меня писать пустые миграции в обратную сторону, когда я не хочу). Зато нужна поддержка PostgreSQL. Ещё было бы классно, если бы библиотека миграции поддерживала asyncio. Да, для самой миграции асинхронность ничего не даёт, но это позволяет не держать в зависимостях две реализации драйвера БД - асинхронный и синхронный.

Аналог из мира Java - Flyway.

 , ,

KivApple
()

Пометить ВСЕ письма как прочитанные

Форум — General

Имеется ящик Gmail. И там скопились буквально тысячи непрочитанных писем (а если быть точным - более 14 тысяч писем). Я их в любом случае никогда уже не прочитаю (да и информация там неактуальная), но из-за них счётчик новых писем не отражает реально новые письма, которые мне нужно прочитать (вообще, это следствие в том числе всяких мусорных рассылок от кучи сайтов, но я недавно навёл порядок и отписался практически от всего хлама, так что теперь мне поступает не слишком много писем в день).

Можно ли как-то через интерфейс Gmail отметить как прочитанные ВСЕ письма или, например, ВСЕ письма старше какой-то даты? Не выделять по 50 штук (это всё равно больше сотни страниц) и жать «отметить как прочитанные», а вот прям все разом.

Если Gmail на такое не способен (хотя, уверен, весьма распространённый use-case), то может быть есть IMAP-совместимый почтовый клиент, который способен на такое? Или даже отдельная утилита вида «дай мне параметры подключения к IMAP и я отмечу прочитанными все письма, которые найду».

Вообще, было бы удобно, если бы письма отмечались как прочитанные после какого-то таймаута. Если у меня не дошли руки прочитать письмо за 1-2 недели, значит не дойдут уже никогда, а потом и информация из письма устареет. Но это уже совсем утопия.

 ,

KivApple
()

CMake: Получить список определений препроцессора

Форум — Development

В ходе компиляции проекта CMakeLists.txt запускает мой Python скрипт, который парсит некоторые исходники и извлекает оттуда некоторую мета-информацию (которая затем используется для генерации части других исходников проекта). Проблема в том, что для корректного парсинга исходников нужно знать объявленные директивы препроцессора. Например, в релизном билде CMAKE добавляет определение NDEBUG. Также какие-то определения могут добавлять используемые библиотеки и, наконец, я сам через add_definitions и target_add_definitions.

Хочется получить полный набор определений, которые добавлены именно через CMake, дефолтные определения компилятора не интересны, потому что я выполняю препроцессинг его же силами (с помощью опции -E) и с ними проблем нет.

get_property(GLOBAL_COMPILE_DEFINITIONS GLOBAL PROPERTY COMPILE_DEFINITIONS)
get_property(DIR_COMPILE_DEFINITIONS DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY COMPILE_DEFINITIONS)
get_property(TARGET_COMPILE_DEFINITIONS TARGET MyProgram PROPERTY COMPILE_DEFINITIONS)
message(STATUS "COMPILE_DEFINITIONS ${TARGET_COMPILE_DEFINITIONS} ${DIR_COMPILE_DEFINITIONS} ${GLOBAL_COMPILE_DEFINITIONS}")

Выдаёт мне объявления добавленные с помощью add_definitions и target_add_definitions, но игнорирует встроенные определения CMake такое как NDEBUG в релизной сборке. Можно ли как-то получить NDEBUG или только хардкодить проверку типа билда? Насколько мой способ получения в целом полный, не пропустит ли он как-нибудь хитро добавленное определение какой-нибудь гипотетической библиотекой?

 ,

KivApple
()

Приложения и сервисы для изучения грамматики иностранного языка

Форум — Talks

Вот учу я, допустим, французский. Есть всякие приложения типа Duolingo, Busuu. У них сильная сторона - изучение лексики, разбор конкретных ситуаций вида «что говорить, если я попал в аэропорт» и «как рассказать как я провёл лето». Просмотр фильмов учит понимать на слух и опять же лексике. Чтение книг учит в первую очередь лексике. А есть ещё такая штука как грамматика. С каким окончанием надо писать глагол partir в третьем лице единственном числе во времени imparfait. И вот такие штуки можно только выучить.

Однако, я до сих пор не встречал комплексных приложений на эту тему. С моей точки зрения идеальное приложение должно работать так:

- Хочу поучить числительные - Тебе показывают число записанное цифрами и ты должен записать его буквами. Число может быть большим - с сотнями, тысячами и даже миллионами. Число случайное, а не один и тот же тест из десяти вопросов. Может быть обратная задача - тебе показывают число записанное буквами и предлагают написать его цифрами.

- Хочу поучить спряжения глаголов для уровня A1/A2/B1/B2/etc (от уровня зависит набор доступных времён, либо можно просто выбрать какие времена интересны). - Тебе показывают случайно выбранный инфинитив глагола, личное местоимение и время. Ты должен написать глагол правильно согласовав. На лёгком уровне сложности могут подсказать группу глагола, на сложном должен сам понять.

То же самое для сопряжения прилагательных (род и число) и т. п.

С алгоритмической точки зрения нет проблем составлять вопросы из кусочков. Достаточно иметь размеченную БД тех же глаголов с указанием правил спряжения (на том же wikitonary есть такая информация, плюс куча других сайтов). Но... Таких приложений нет. Предлагают учить лексику, а грамматика сама как-нибудь когда-нибудь подтянется. Но ведь она тоже важна.

Можно найти учебник с упражнениями, но эти упражнения потом должен кто-то проверять, а у приложений и сервисов плюс в мгновенной реакции правильно/неправильно. Это гораздо эффективнее для обучения.

Может быть уже есть такие штуки? А то у меня уже от безысходности часаться руки по мере изучения языка писать процедурные генераторы вопросов такого вида.

P. S. Только не надо говорить, что грамматика не нужна - есть вполне конкретные ситуации, когда она нужна 100% - сдача языкового экзамена для поступления в вуз, трудоустройства, получения ВНЖ/ПМЖ/гражданства и т. д.

P. P. S. Английский ещё относительно легко учится, там одна таблица неправильных глаголов, из которой активно используется меньше половины. А времена строятся как различные комбинаиции употребления to be, have и инфинитива глагола с максимум с парой вариантов концовок. А в том же французском есть группы глаголов, рода, времена и это всё с кучей исключений для каждого случая.

 иностранные языки

KivApple
()

RSS подписка на новые темы