LINUX.ORG.RU

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

 ,


0

5

Допустим, у нас есть некая мутабельная структура данных (в данном случае это просто число, но это лишь для простоты примера), которую мы хотим шарить между потоками. При этом мы хотим сделать свою 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 (всего исправлений: 1)

Я правильно понимаю, что ты хочешь сделать структуру, которая при блокировке вернет новую структуру с референсом на то же самое значение?

cumvillain
()
Ответ на: комментарий от cumvillain

Я хочу сделать структуру, которая при блокировке будет держать в себе MutexGuard и, соответственно, иметь в своих методах возможность читать/писать в защищаемую структуру (так что да, по факту обёртка содержит в себе ссылку на исходное значение, но в первую очередь косвенно через потроха MutexGuard). А при Drop этой структуры блокировка отпустится.

Поле _data у обёртки есть исключительно из соображений, чтобы память не освободилась, пока Mutex захвачен (у Arc была хотя бы одна ссылка). Если это будет обеспечиваться как-то ещё, то можно это поле убрать.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 3)

В любом случае, выглядит так, как будто ты хочешь брать лок в fn lock() и сохранять MutexGuard в структуре уже оттуда.

cumvillain
()
Ответ на: комментарий от cumvillain

Тем, что вытаскивает наружу не объект, а обёртку над ним, который предоставляет доступ через свои методы.

Вообще, это для воксельного игрового движка и идея в том, что каждый чанк представляется Arc<Mutex<VoxelChunk>>, они хранятся в Mutex<HashMap<(i32, i32, i32), Arc<Mutex<VoxelChunk>>>. Соответственно, есть возможность заблокировать 1 чанк, а есть возможность заблокировать 3х3х3 чанков (некоторые операции над чанком требуют иметь возможность подсмотреть, что там у соседей). Для первого случая, действительно, никакие обёртки не нужны, можно сделать все методы в самом VoxelChunk. А вот для второго сценария обёртка необходима в виде структуры, которая держит сразу 27 блокировок и содержит метод чтения/записи вокселей по относительным координатам, который диспатчит операцию в нужный чанк из захваченных.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от cumvillain

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

https://rust.godbolt.org/z/1745vEov5

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 2)
use std::sync::{Arc, Mutex, MutexGuard};

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.lock().unwrap())
    }
}

struct LockedSharedData<'a> {
    data: MutexGuard<'a, i32>
}

impl<'a> LockedSharedData<'a> {
    pub fn new<'b>(value: MutexGuard<'b, i32>) -> Self
    where 'b: 'a
    {
        LockedSharedData {
            data: value
        }
    }

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

fn main() {
    let sd = SharedData::new(42);
    let lsd = sd.lock();
    println!("{}", lsd.get());
}
cumvillain
()
Ответ на: комментарий от cumvillain

Да, это работает, но моя конечная цель более сложная структура данных и для неё уже не работает.

Rust и обёртка над блокировкой (комментарий)

Rust и обёртка над блокировкой (комментарий)

Я не случайно пытался выполнять манипуляции именно над клоном Arc, потому что оригинал будет уничтожен при отпускании Mutex HashMap.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 2)

В то время как цпп-бояре пилят бизнеслогику, растоводы всей толпой пытаются взять блокировку. Не продакшен-риди язык. АДНАЗНАЧНА

ox55ff ★★★★★
()
Ответ на: комментарий от cumvillain

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

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от ox55ff

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

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от cumvillain

Мне не нравится две вещи:

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

- Прибито гвоздями к Mutex. А я потом захочу RWLock, просто это ещё сильнее усложнило бы минимальный пример.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 3)
Ответ на: комментарий от rupert

Ну как бы да. Только я хочу обернуть имеющийся RAII MutexGuard в свою дополнительную обёртку.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Смотри в чем дело. HashMap::get() возвращает тебе ссылку на Arc, которую ты пытаешь бампать через clone() и унести. С точки зрения borrow checker’а это выглядит как полная дичь, потому что никто не мешает тебе взять и грохнуть весь HashMap строчкой ниже, например. Или грохнуть его в соседней функции.

cumvillain
()
Ответ на: комментарий от cumvillain

Не согласен. Я делаю clone у Arc и после этого уже не важно, что будет с HashMap, ведь у меня есть независимая от него ссылка в локальной переменной, а сам объект выделен в куче. Проблема в том, что новая ссылка сидит в локальной переменной, которая умрёт при выходе из функции, а я хочу вернуть блокировку, которая от неё зависит. Мне бы вернуть их обоих, но у Rust проблемы со структурами, которые содержат внутренние ссылки.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от ox55ff

В то время как цпп-бояре ищут причину сегфолта или пытаются понять, что за ошибка шаблона в простыне от компилятора, растоводы всей толпой пытаются взять блокировку. Не продакшен-риди язык. АДНАЗНАЧНА

Я исправил твою опечатку, не благодари

Aswed ★★★★★
()
Ответ на: комментарий от ox55ff

растоводы всей толпой пытаются взять блокировку. Не продакшен-риди язык. АДНАЗНАЧНА

Вот просто с языка снял.

no-such-file ★★★★★
()
Ответ на: комментарий от KivApple

Отладка шаблонной лапши ничем не лучше войн с borrow-чекером

Да. Но если не видно разницы, зачем платить больше? (С)

no-such-file ★★★★★
()

Там еще в предыдущем треде говнометы никак не остановятся, ты новый подкидываешь. Совести у тебя нет.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

В той теме первоначальный натиск Царя был отбит и Хрюндель перешёл в контрнаступ. Панует. Тревожно за цпп 🙏. Нужно открывать второй фронт, чтобы защитить его честь.

ox55ff ★★★★★
()
Ответ на: комментарий от KivApple

Arc выделен как часть HashMap. Arc::clone() только бампает счетчик в уже выделенной памяти. Так что если ты вдруг чистишь HashMap, у тебя на руках невалидный указатель на Arc. Это первое. Второе, у тебя доступ к &HashMap через MutexGuard. Суть MutexGuard в том, что доступ к &HashMap есть только во время жизни MutexGuard. Ну и в итоге у тебя примерно такие зависимости:

&Arc -> &HashMap -> &HashMap -> &MutexGuard

cumvillain
()
Ответ на: комментарий от cumvillain

Неа, у Arc внутри Box:

https://moshg.github.io/rust-std-ja/src/alloc/arc.rs.html#282

То есть данные хранятся в куче, а Arc лишь указатель на них. Можно иметь много клонов Arc и данные будут жить, пока жив последний клон.

Ты путаешь Arc с RefCell, который реально хранит внутри сами данные и при его смерти данные тоже умирают.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от cumvillain

Верно. Но клонированный Arc уже никак не зависит от оригинального Arc, который лежит в HashMap - https://moshg.github.io/rust-std-ja/src/alloc/arc.rs.html#701.

Я и хочу его клонировать, а потом отпустить Mutex у HashMap.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от ox55ff

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

Это невозможно. Вероятно царек прервался, чтобы подмести свой двор и прилегающий сквер, всё-таки не споры на ЛОР же его кормят.

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

Это невозможно.

Спор с царем как ремонт: можно прервать, невозможно закончить :)

Вероятно царек прервался, чтобы подмести свой двор и прилегающий сквер, всё-таки не споры на ЛОР же его кормят.

Или его вернули к приему препаратов.

eao197 ★★★★★
()
Ответ на: комментарий от no-such-file

Ты либо пытаешься обмануть меня либо самого себя. Ошибки borrow checker-а в примере наверху. Там буквально 2 пункта. Где и почему. И даже конструкция в коде подсвечена для неумеющих ориентироваться. Теперь посмотрим как выглядит типичаня ошибка шблонизатора с++

$ cat main.cpp
#include<vector>
#include<unordered_set>

using namespace std;

int main(int x, char **y) {
    unordered_set<vector<int>> vec;
    return 0;
}
$ g++ main.cpp

Так, что тут у нас?

main.cpp: In function ‘int main(int, char**)’:
main.cpp:7:32: error: use of deleted function ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set() [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >]’
    7 |     unordered_set<vector<int>> vec;
      |                                ^~~
In file included from /usr/include/c++/10/unordered_set:47,
                 from main.cpp:2:
/usr/include/c++/10/bits/unordered_set.h:135:7: note: ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set() [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >]’ is implicitly deleted because the default definition would be ill-formed:
  135 |       unordered_set() = default;
      |       ^~~~~~~~~~~~~
/usr/include/c++/10/bits/unordered_set.h:135:7: error: use of deleted function ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable() [with _Key = std::vector<int>; _Value = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
In file included from /usr/include/c++/10/unordered_set:46,
                 from main.cpp:2:
/usr/include/c++/10/bits/hashtable.h:451:7: note: ‘std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_Hashtable() [with _Key = std::vector<int>; _Value = std::vector<int>; _Alloc = std::allocator<std::vector<int> >; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’ is implicitly deleted because the default definition would be ill-formed:
  451 |       _Hashtable() = default;
      |       ^~~~~~~~~~
/usr/include/c++/10/bits/hashtable.h:451:7: error: use of deleted function ‘std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::_Hashtable_base() [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’
In file included from /usr/include/c++/10/bits/hashtable.h:35,
                 from /usr/include/c++/10/unordered_set:46,
                 from main.cpp:2:
/usr/include/c++/10/bits/hashtable_policy.h:1791:5: note: ‘std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::_Hashtable_base() [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _Equal = std::equal_to<std::vector<int> >; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _Traits = std::__detail::_Hashtable_traits<true, true, true>]’ is implicitly deleted because the default definition would be ill-formed:
 1791 |     _Hashtable_base() = default;
      |     ^~~~~~~~~~~~~~~
/usr/include/c++/10/bits/hashtable_policy.h:1791:5: error: use of deleted function ‘std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, std::__detail::_Default_ranged_hash, true>::_Hash_code_base() [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing]’
/usr/include/c++/10/bits/hashtable_policy.h:1368:7: note: ‘std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _H1, _H2, std::__detail::_Default_ranged_hash, true>::_Hash_code_base() [with _Key = std::vector<int>; _Value = std::vector<int>; _ExtractKey = std::__detail::_Identity; _H1 = std::hash<std::vector<int> >; _H2 = std::__detail::_Mod_range_hashing]’ is implicitly deleted because the default definition would be ill-formed:
 1368 |       _Hash_code_base() = default;
      |       ^~~~~~~~~~~~~~~
/usr/include/c++/10/bits/hashtable_policy.h:1368:7: error: use of deleted function ‘std::__detail::_Hashtable_ebo_helper<_Nm, _Tp, true>::_Hashtable_ebo_helper() [with int _Nm = 1; _Tp = std::hash<std::vector<int> >]’
/usr/include/c++/10/bits/hashtable_policy.h:1112:7: note: ‘std::__detail::_Hashtable_ebo_helper<_Nm, _Tp, true>::_Hashtable_ebo_helper() [with int _Nm = 1; _Tp = std::hash<std::vector<int> >]’ is implicitly deleted because the default definition would be ill-formed:
 1112 |       _Hashtable_ebo_helper() = default;
      |       ^~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/10/bits/hashtable_policy.h:1112:7: error: use of deleted function ‘std::hash<std::vector<int> >::hash()’
In file included from /usr/include/c++/10/bits/stl_bvector.h:61,
                 from /usr/include/c++/10/vector:68,
                 from main.cpp:1:
/usr/include/c++/10/bits/functional_hash.h:101:12: note: ‘std::hash<std::vector<int> >::hash()’ is implicitly deleted because the default definition would be ill-formed:
  101 |     struct hash : __hash_enum<_Tp>
      |            ^~~~
/usr/include/c++/10/bits/functional_hash.h:101:12: error: no matching function for call to ‘std::__hash_enum<std::vector<int>, false>::__hash_enum()’
/usr/include/c++/10/bits/functional_hash.h:82:7: note: candidate: ‘std::__hash_enum<_Tp, <anonymous> >::__hash_enum(std::__hash_enum<_Tp, <anonymous> >&&) [with _Tp = std::vector<int>; bool <anonymous> = false]’
   82 |       __hash_enum(__hash_enum&&);
      |       ^~~~~~~~~~~
/usr/include/c++/10/bits/functional_hash.h:82:7: note:   candidate expects 1 argument, 0 provided
/usr/include/c++/10/bits/functional_hash.h:101:12: error: ‘std::__hash_enum<_Tp, <anonymous> >::~__hash_enum() [with _Tp = std::vector<int>; bool <anonymous> = false]’ is private within this context
  101 |     struct hash : __hash_enum<_Tp>
      |            ^~~~
/usr/include/c++/10/bits/functional_hash.h:83:7: note: declared private here
   83 |       ~__hash_enum();
      |       ^
In file included from /usr/include/c++/10/bits/hashtable.h:35,
                 from /usr/include/c++/10/unordered_set:46,
                 from main.cpp:2:
/usr/include/c++/10/bits/hashtable_policy.h:1112:7: error: use of deleted function ‘std::hash<std::vector<int> >::~hash()’
 1112 |       _Hashtable_ebo_helper() = default;
      |       ^~~~~~~~~~~~~~~~~~~~~
<тут еще куча строк, которые в сообщение не влезают>
Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Там буквально 2 пункта. Где и почему

Тем не менее ТС имеет с этим проблемы. Наверное всё не так просто, правда же?

Ну и да, кого ты пытаешься обмануть? Написано же

main.cpp: In function ‘int main(int, char**)’:
main.cpp:7:32: error: use of deleted function ‘std::unordered_set<_Value, _Hash, _Pred, _Alloc>::unordered_set() [with _Value = std::vector<int>; _Hash = std::hash<std::vector<int> >; _Pred = std::equal_to<std::vector<int> >; _Alloc = std::allocator<std::vector<int> >]’

остальную портянку можно не читать. Указано место и проблема. Я на крестах хрен знает сколько времени ничего не писал, и то сразу сообразил, что такого конструктора нет и нужно ещё хэш-функцию передать. Любой школьник может загуглить решение за 5 минут.

no-such-file ★★★★★
()
Ответ на: комментарий от no-such-file

Лол ты только что буквально сравнил 29 строчек с учетом рисунка в консоли с гигантской портянкой, только потому что «если поискать в портянке, то можно найти ответ» и сказал, что одно и то же. Собсна возвращаюсь к вопросу, ты пытаешься обмануть меня или самого себя?

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Так себе аргумент. Догадываешься, что печать ошибок полностью зависит от компиляторописателей? Количество информации всегда можно порезать в отличии от её отсутсвия. Кто тебе мешает cделать обертку для компиляции?

clang++ 2.cpp 2>&1 | sed 's/<.*>/<...>/;s/.*\/\([^:]*\)/\1/'  

2.cpp:7:32: error: call to implicitly-deleted default constructor of 'unordered_set<...>'
    unordered_set<...> vec;
                               ^
unordered_set.h:135:7: note: explicitly defaulted function was implicitly deleted here
      unordered_set() = default;
      ^
unordered_set.h:100:18: note: default constructor of 'unordered_set<...>' is implicitly deleted because field '_M_h' has a deleted default constructor
      _Hashtable _M_h;
                 ^
hashtable.h:531:7: note: explicitly defaulted function was implicitly deleted here
      _Hashtable() = default;
      ^
hashtable.h:183:7: note: default constructor of '_Hashtable<...>' has a deleted default constructor
    : public __detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal,
      ^
hashtable_policy.h:1674:7: note: explicitly defaulted function was implicitly deleted here
      _Hashtable_base() = default;
      ^
hashtable_policy.h:1632:7: note: default constructor of '_Hashtable_base<...>' has a deleted default constructor
    : public _Hash_code_base<_Key, _Value, _ExtractKey, _Hash, _RangeHash,
      ^
hashtable_policy.h:1261:7: note: explicitly defaulted function was implicitly deleted here
      _Hash_code_base() = default;
      ^
hashtable_policy.h:1240:7: note: default constructor of '_Hash_code_base<...>' has a deleted destructor
    : private _Hashtable_ebo_helper<...>
      ^
hashtable_policy.h:1176:7: note: destructor of '_Hashtable_ebo_helper<...>' has a deleted destructor
    : private _Tp
      ^
functional_hash.h:102:19: note: destructor of 'hash<...>' has an inaccessible destructor
    struct hash : __hash_enum<...>
                  ^
1 error generated.

Значительно меньше инфы без подробностей, которые самые маленькие не осиливают. Хотя в случае с СТД либой действительно было бы неплохо сделать какой-то флаг, который будет активировать проверки static_asserta’ми в самом начале процесса специализации библиотечных сущностей без детализации до царя гороха. Но всё это в руках реализующих стандартную либу, сами кресты ни к чему здесь не обязывают. На горизонте глобальное причесывание std в связи с переходом на модули, может и до этого руки дойдут.

kvpfs ★★
()

Я как раз писал нечто подобное (игру). Именно такую структуру сделать без unsafe невозможно. Но для того что бы подглядывать в соседние чанки я просто изменил подход: я лучше продумал алгоритм и распределил группы «чанков» в батчи, где блокировался целевой чанк и соседние одновременно.

anonymous-angler ★☆
()
Последнее исправление: anonymous-angler (всего исправлений: 1)
Ответ на: комментарий от kvpfs

Главная претензия сохраняется. Ошибка в коде-то в том, что vector<int> нехешируемый. А из объяснения компилятора это вообще неочевидно. Ни

unordered_set.h:135:7: note: explicitly defaulted function was implicitly deleted here
      unordered_set() = default;

ни

functional_hash.h:102:19: note: destructor of 'hash<...>' has an inaccessible destructor
    struct hash : __hash_enum<...>

Не сообщает в чем именно проблема. Если не знаешь что искать, то без гуглежа не разберешься. Да и вообще, если тебе надо выхлоп компилятора прогонять через sed, что бы в нем разобраться, то очевидно выхлоп компилятора говно. Вот ты вот знаешь еще какой-нибудь ЯП где сообщение об ошибке от компилятора надо прогонять через какие-то утилиты, что бы его можно было прочитать. Я вот не знаю.

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Ошибка в коде-то в том, что vector<int> нехешируемый. А из объяснения компилятора это вообще неочевидно.

Очевидно, просто нужно долистать до сообщения, где об этом сказано прямо:

/usr/include/c++/10/bits/hashtable_policy.h:1112:7: error: use of deleted function ‘std::hash<std::vector<int> >::hash()’

И, надо сказать, что приведенная вами простыня из сообщений – это еще очень и очень хорошо. Мне за последние несколько недель пару-тройку раз попадались простыни, в которых указывались на ошибки в коде сторонней библиотеки (типа невозможно сгенерировать конструктор для unique_ptr<T>, где T – это шаблонный тип из чужой либы), но было совершенно непонятно что именно привело к возникновению ошибки компиляции. Ни одно из сообщений компилятора с маркером error не давало подсказки. Единственное полезное в выхлопе компилятора было note с номером строки из моего файла, откуда начались проблемы.

Вот это да, вот это реальная проблема.

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от eao197

Так в случае нормальной реализации строгой типизации это бы и былае единственная ошибка в сообщении от компилятора, а не где-то там в простыне в поле note. Ты же сам сейчас описываешь проблему на которую я жалуюсь в сообщении выше. 90% выхлопа компиляторов С++ это мусор. А где-то там, есть одна строчка, где в поле note описывается реальная проблема. Так не должно быть.

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Так не должно быть.

В IDE(по крайней мере в Visual Studio) ошибки показываются одной строкой и по двойному клику переходят на строчку кода с ошибкой.

Возможно другие IDE тоже пытаются упростить сообщения об ошибках.

Хотя иногда всё же требуется увидеть полную ошибку…

fsb4000 ★★★★★
()
Ответ на: комментарий от kvpfs

Я кстати еще несогласен с утверждением про то, что печать ошибок полностью зависит от компиляторописателей. Я не думаю, что создатели g++ или clang такие тупые, что не могут в нормальное описание ошибок. Проблема тут в стандарте. В том, что у тебя есть несколько функций с одинаковым именем, а компилятор, по неочевидным причинам выбирает какую-то из них, и это можнет быть не то, что тебе нужно. А причины выбора неочевидны, потому что выбор перегруженной функции происходит исходя из типов аргументов, и указывать можно даже такие мелкие детали как const или не const. Одновременно с этим в плюсах нет строгой типизации, и в любой момент может быть неявное приведение типов. Поэтому вообще не очевидно какая функция/конструтор привела к ошибке компиляции. Поэтому все компиляторы выдают такую простыню. Потому что без нее не разобраться. Тут проблема не в реализации, а язык плохой by design.

Aswed ★★★★★
()
Ответ на: комментарий от fsb4000

Ну это хорошо, что IDE решают такие проблемы. Мои претензии не IDE. А к тому, что без спецсредств читать сообщения компиляторов плюсов очень сложно.

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Глупость, всё зависит от реализации, вот на коленках сделал:

$ cat 1.cpp

#include <unordered_map>
#include <vector>
using namespace std;

template<typename T>
concept Hashable = requires(T a) {
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};

template <Hashable K, typename V>
class Um : public unordered_map<K, V> {
};

int main() {
        Um<vector<int>, int> m;
}

$ clang++ -std=c++20 1.cpp
1.cpp:15:2: error: constraints not satisfied for class template 'Um' [with K = std::vector<int>, V = int]
        Um<vector<int>, int> m;
        ^~~~~~~~~~~~~~~~~~~~
1.cpp:10:11: note: because 'std::vector<int>' does not satisfy 'Hashable'
template <Hashable K, typename V>
          ^
1.cpp:7:20: note: because 'std::hash<T>({})(a)' would be invalid: temporary of type '__hash_enum<std::vector<int>>' has private destructor
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
                   ^
1 error generated.

Так что всё зависит от желания причесать libstdc++, учитывая переход на модули std либы - её и так будут причесывать, может и здесь что сдвинется.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от Aswed

Так в случае нормальной реализации строгой типизации это бы и былае единственная ошибка в сообщении от компилятора, а не где-то там в простыне в поле note

Тут смешано сразу несколько вещей.

Во-первых, нормальная статическая (и даже строгая в моем случае) типизация как раз и есть. Поэтому я и получаю ошибку во время компиляции, а не в рантайме, как в каких-нибудь Ruby/Python.

Во-вторых, единственная ошибка. Т.к. я выходец из Паскаля, то мне всегда было непонятно, почему C и C++ стараются выдать максимум ошибок, а не останавливаются на первой же (т.к. процентах в 90 случаев все остальное – это уже последствия первой же ошибки). Но, видимо, тут наследие тяжких времен больших машин и пакетной компиляции.

Хотя, надо сказать, что при работе с шаблонами эти самые простыни из ошибок оказываются полезными, т.к. причина часто оказывается описана где-то в 10-ом или 15-ом сообщении об ошибке.

А где-то там, есть одна строчка, где в поле note описывается реальная проблема. Так не должно быть.

Тут проблема не в копиляторах, а в том, что в C++ шаблоны построены по принципу «утиной типизации»: т.е. когда я пишу T<U>, то компилятор не может сразу понять, удовлетворяет ли U всем требованиям, которые выдвигает T. Компилятору приходится выполнить инстанциирование всех использованных из T<U> методов и уже в процессе инстанциирования проверять все ли нормально (по сути, сперва генерируется новый «исходник», который затем транслируется, хотя это очень и очень грубо).

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

Но тут мы попадаем на поляну «если бы у бабушки был…» C++ без его шаблонов с «утиной типизацией» не был бы C++ом и кому-то (мне, например) был бы не интересен.

В C++20 для решения этой проблемы добавлены концепты. Посмотрим, что из этого получится на практике. Говорят, что местами сильно помогает.

eao197 ★★★★★
()
Ответ на: комментарий от kvpfs

Т.е. по-твоему это нормально когда для использования стандартной библиотеки языка нужно каждый класс вот так самому переопределять?

template<typename T>
concept Hashable = requires(T a) {
    { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;
};
Aswed ★★★★★
()
Ответ на: комментарий от eao197

Во-первых, нормальная статическая (и даже строгая в моем случае) типизация как раз и есть. Поэтому я и получаю ошибку во время компиляции, а не в рантайме, как в каких-нибудь Ruby/Python.

Не. Вот тут как раз ты путаешь. В С++ статическая типизация, но нестрогая. А вот в python как раз таки строгая, хоть и динамическая. Если в питоне ты в функцию котороая работает только с int передаешь float(например range), то ты получишь ошибку. А в c++ начнется приведение типов, даже если ты этого не указывал. Это нифига не строгая типизация.

Хотя, надо сказать, что при работе с шаблонами эти самые простыни из ошибок оказываются полезными, т.к. причина часто оказывается описана где-то в 10-ом или 15-ом сообщении об ошибке.

Так я об этом и говорю. В «нормальных языках» при ошибке компилятора только эта нужная строчка и выводится и перед нет нет 15ти строчек мусора.

Тут проблема не в копиляторах, а в том, что в C++ шаблоны построены по принципу «утиной типизации»:

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

Aswed ★★★★★
()
Ответ на: комментарий от Aswed

Не. Вот тут как раз ты путаешь. В С++ статическая типизация, но нестрогая. А вот в python как раз таки строгая, хоть и динамическая. Если в питоне ты в функцию котороая работает только с int передаешь float(например range), то ты получишь ошибку. А в c++ начнется приведение типов, даже если ты этого не указывал. Это нифига не строгая типизация.

Это вы что-то путаете. Приведение типов вот в такой ситуации:

void handle_value(float v) {...}

const int my_val = 42;

handle_value(my_val); // Здесь имеем int -> float

не есть слабая типизация. Здесь происходит конвертация из int во float согласно правилам языка. Т.е. и статическая, и строгая типизация.

Слабая типизация проявляется вот где:

void handle_value(float v) {...}

const int my_val = 42;

handle_value(*((const float *)&my_val)); // Здесь имеем UB

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

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

Как бы лучше при тех же самых возможностях никто пока не сделал. Взять тот же Rust, какие-то проблемы в нем решили, но я мог бы назвать его говном просто потому, что его авторы ниасилили нормальный ООП. Так что толку мне от точных сообщениях об ошибках от rustc, если в этом самом Rust-е я не могу использовать полноценное и нормальное ООП?

eao197 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.