Pull to refresh
2
0

User

Send message

В C++17 можно манипулировать узлами node-based контейнеров — извлечь, поменять ключ/значение и вставить назад (или в другой контейнер того же типа)


https://en.cppreference.com/w/cpp/container/set/extract


std::set<int> cont{1, 2, 3};
auto nh = cont.extract(1);
nh.value() = 4; 
cont.insert(move(nh));

Насколько я понимаю, gcc и clang всегда используют минимум 2 слова, msvc пытается варьировать 1-3 слова, в зависимости от класса. Реймонд Чен писало про это: Pointers to member functions are very strange animals

Адрес чего? Выше вы привели код, из которого можно понять, что там происходит получение значения машинного адреса указателя на указатель на функцию-член класса.


В таком случае указатель на указатель будет действительно sizeof(void*), но это уже следующий уровень косвенности.

Доказательств привести не могу, но теоретически оптимизатор может знать реальный адрес функции после девиртуализации какого-то экземпляра класса или link-time optimization.

Только если вам очень повезет, и указатель на функцию-член действительно будет размером sizeof(void*).


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

Насколько я помню, в COM для расширения функциональности и сохранения обратной совместимости с существующим кодом создавали новый интерфейс-наследник:


struct Interface
{
    virtual ~Interface() = default;
    virtual void a() = 0;
};

class Implementation : public Interface
{
    virtual void a() override;
};

// в следующей версии
struct Interface2 : Interface
{
    virtual void b() = 0;
};

class Implementation2 : public Interface2, Implemetation
{
    virtual void a() override;
};

Еще можно почитать на ту же тему статью Interface Versioning in C++

Похоже, что Алексей Бабушкин просто позаимствовал идею πfs https://github.com/philipl/pifs

Точно, через пару лет этот программист напишет свою 2-ю версию многопоточной очереди. Ведь предыдущие поколения программистов были дураки, боролись с небезопасным С и страдали ерундой последние лет 20.

Компилятор Rust действительно делает переупорядочивание полей в структурах? И сможет разложить поля по разным кэш-линиям? Смахивает на городскую легенду. Например, в стандарте C++ тоже такое упоминается, но на практике это редко происходит, может быть только переупорядочивание целых секций, объявленых как public или private, иногда случается. Так что это до сих пор обязанность разработчика, думать о расположении данных при многопоточном доступе к ним:


struct Queue {
    // должно начинаться с новой кэш-линии  
    alignas(std::hardware_destructive_interference_size) std::atomic<uint32_t> head; 
    // так же
    alignas(std::hardware_destructive_interference_size) std::atomic<uint32_t> tail;
    // так же
    alignas(std::hardware_destructive_interference_size) Task buffer[mask+1];
    // по идее не должно никогда меняться
    const uint32_t mask;
};

Интересно, можно ли в Rust управлять выравниванием данных в структурах? Потому что в такой структуре:


struct Queue {
    /// Concurrently updated by many threads.
    head: AtomicU32,

    /// Only updated by producer thread but read by many threads.
    tail: AtomicU32,

    /// Masks the head / tail position value to obtain the index in the buffer.
    mask: usize,

    /// Stores the tasks.
    buffer: Box<[MaybeUninit<Task>]>,
}

head и tail вероятнее всего будут лежать в одной кэш-линии, классический вид false cache sharing.


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

Один программист по имени Wojciech Muła публикует статьи по практическому применению SIMD: http://0x80.pl/articles/index.html


Мне нравится его подход со сравнением разных реализаций для одной конкретной задачи.

Делать и использовать нужно то, что подходит под требования проекта. На мой взгляд, edba выглядит лучше в качестве универсальной библиотеки. В ней, по крайней мере используется C++11 без ручного управления памятью и deprecated
auto_ptr.


Статей про плюсы/минусы ORM и без меня уже написано за последние лет 20 много, так что я, пожалуй, не буду увеличивать энтропию.


Я глянул на код вашей статьи. В db_pool возможно двойное освобождение памяти — сырой указатель и отсутствие конструктора копирования гарантируют это.


В user_info рукопашный код для перегона


std::vector<int> friends; // айдишники друзей

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


В test.cxx в строках


 // нам нужно для каждого бэкенда, указать правильный тип авто-счётчика для поля id
      if (sql.get_backend_name() == "postgresql") query_str += " SERIAL ";
      else if (sql.get_backend_name() == "mysql") query_str += " INT AUTO_INCREMENT ";

вы показываете суть универсального доступа к БД — и вынуждены бороться с различиями диалектов SQL.

Да зрелый, более точное определение для проекта с датой последнего релиза 2015. Как и парочка упомянутых выше. Вот еще один зрелый проект для коллекции: https://github.com/pmed/sqlitepp :)


По моему небольшому опыту, сама идея встраивания SQL-подобного кода в С++ программу в конечном итоге заканчивается спуском к деталям реализации конкретной БД и вызову конкретных функций коннектора этой БД.

Еще парочка похожих проектов примерно такого же состояния заброшенности:


Спасибо, но я подожду С++20, там будет bool map::contains(key) https://en.cppreference.com/w/cpp/container/map/contains, с поддержкой transparent comparator.


В Boost с версии 1.67 есть нечто подобное, без дополнительных макросов: https://www.boost.org/doc/libs/1_68_0/libs/hof/doc/html/include/boost/hof/infix.html

Недавно Andrzej Krzemieński опубликовал пару статей про создание собственных error_code и error_condition:
Your own error code
Your own error condition


На мой взгляд, там более понятно объясняется разница между ними.

Похоже маловато константности, может constexpr поможет.

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


Вообще, локали одно из самых тормозных мест в стандартной библиотеке, и в плане компиляции, и времени выполнения. Так что автора cppformat можно понять, что он не хочет с ними связываться.

Information

Rating
Does not participate
Registered
Activity