Re[6]: off-topic: диапазонный массив
От: Sm0ke Россия ksi
Дата: 21.08.23 16:35
Оценка:
Здравствуйте, sergii.p, Вы писали:

SP>Здравствуйте, Sm0ke, Вы писали:


S>>Завести тип индекса с диапазоном, который в конструкторе проверяет выход за границы.

S>>Тогда обращение к элементу по этому индексу будет boundary safe без дополнительных проверок.

SP>это неочевидно:


SP>
SP>std::vector<int> mut_v = {1, 2, 3};
SP>const auto correct_idx = calc_index(mut_v, 0);
SP>mut_v.clear();
SP>std::cout << mut_v[correct_idx];
SP>


Дак со стандартным вектором такое не прокатит.
Тут нужен custom fixed sized контейнер.

SP>чтобы это работало, получается надо иметь в языке что-то похожее на borrow checker. Вот в Rust имеет смысл "поесть этот кактус".

Про borrowed checker немного не понял...

SP>Вообще идея Ada на диапазонные значения выглядит немного нежизнеспособно. Звучит круто, а на практике бонусов не видно.

не знаю, не пробовал. может быть вы правы.
Re[4]: * видео на тему
От: sergii.p  
Дата: 21.08.23 16:54
Оценка: 21 (2)
Здравствуйте, Sm0ke, Вы писали:

S>Поясните пожалуйста подробнее

S>Какой ордеринг там может понадобиться?

partial_ordering
Топологическая сортировка

Применение
При помощи топологической сортировки строится корректная последовательность выполнения действий, всякое из которых может зависеть от другого: последовательность прохождения учебных курсов студентами, установки программ при помощи пакетного менеджера, сборки исходных текстов программ при помощи Makefile'ов.

Ну например, без математики тяжело изучать физику и химию. Значит между Математика <=> Физика = less и Математика <=> Химия = less. А вот физику и химию можно изучать параллельно: Физика <=> Химия = unordered. На этом основании можно последовательность обучения построить как (Математика Физика Химия ) так и (Математика Химия Физика ).
Re[7]: off-topic: диапазонный массив
От: sergii.p  
Дата: 21.08.23 20:29
Оценка:
Здравствуйте, Sm0ke, Вы писали:

S>Про borrowed checker немного не понял...


в том смысле, что borrow checker приведённые выше ошибочные конструкции не даст создать

fn main() {
    let mut container = MyContainer::new().push(1).push(2).push(3);
    match try_read_2nd(&container) {
        Ok(v) => println!("Ok {}", v),
        Err(e) => println!("Err {} not in 0..{}", e.incorrect_idx, e.max)
    }
    let incorrect_idx = container.create_index(1).unwrap(); // создать можем без проблем
    container.clear(); // а дальше уже ошибка компилятора cannot borrow `container` as mutable because it is also borrowed as immutable
    println!("Ok {}", container[incorrect_idx]);
}

fn try_read_2nd(container: &MyContainer) -> Result<u8, BoundError> {
    let idx = container.create_index(1)?;
    Ok(container[idx]) // а здесь всё нормально: ссылаемся на константный контейнер. Он поменять размер не может. Можем не проверять на выход за границы.
}


  реализация
use std::vec::Vec;
use core::ops::Index;

#[derive(Debug)]
struct BoundError {
    incorrect_idx: usize,
    max: usize
}

struct MyIndex<'a> {
    i: usize,
    #[warn(dead_code)]
    parent: &'a MyContainer,
}

struct MyContainer {
    inner: Vec<u8>,
}

impl MyContainer {
    fn new() -> MyContainer {
        MyContainer { inner: Vec::new() }
    }
    
    fn push(self, v: u8) -> Self {
        let mut temp = self;
        temp.inner.push(v);
        temp
    }
    
    fn clear(&mut self) {
        self.inner.clear();
    }

    fn create_index(&self, v: usize) -> Result<MyIndex, BoundError> {
        if self.inner.len() == 0{
            return Err(BoundError{incorrect_idx: v, max: 0})
        } 
        let size = self.inner.len() - 1;
        match v {
            i if (0..=size).contains(&i) => Ok(MyIndex { i: v, parent: &self }),
            _ => Err(BoundError{incorrect_idx: v, max: self.inner.len()})
        }
    }
}

impl<'a> Index<MyIndex<'a>> for MyContainer {
    type Output = u8;
    
    fn index(&self, index: MyIndex<'a>) -> &Self::Output {
        self.inner.index(index.i)
    }
}
Re: Свой каст as_type<>
От: Sm0ke Россия ksi
Дата: 17.09.23 11:05
Оценка:
Возвращать из оператора <=> свой тип, и при этом иметь = default позволяет пока лишь clang ...

Оборачиать enum в класс, чтобы добавить туда конструкторы из разных ордериногов
// std::strong_ordering, std::weak_ordering, std::partial_ordering
Мне не очень понравилось. Оператор static_cast перегрузить пока мы тоже не можем.

Захотелось чего-то такого:
void test_switch()
{
  switch( 1 <=> 0 > as_compare )
  {
  case t_compare::n_less :
    std::cout << "less\n"; break;
    
  case t_compare::n_equal :
    std::cout << "equal\n"; break;

  case t_compare::n_greater :
    std::cout << "greater\n"; break;
  }
}

И у меня получилось это осуществить.

Пришлось завести структуру преобразования.
template <typename Result>
struct t_cast;

Которую можно будет специализировать для своих типов (или любых других)

Например для енума результатов сравнения от всех трёх ордерингов
enum class t_compare
{
  n_none,
  n_less,
  n_equal_partial,
  n_equal_weak,
  n_equal,         // strong
  n_greater,
  n_unordered
};


Делаю специализацию у t_cast (с перегруженным оператором круглые скобки)
template <>
struct t_cast<t_compare>
{
  using result_type = t_compare;

  constexpr result_type
    operator () (std::strong_ordering p) const
  {
    return
      p < 0 ? result_type::n_less :
      p > 0 ? result_type::n_greater : result_type::n_equal;
  }

  constexpr result_type
    operator () (std::weak_ordering p) const
  {
    return
      p < 0 ? result_type::n_less :
      p > 0 ? result_type::n_greater : result_type::n_equal_weak;
  }

  constexpr result_type
    operator () (std::partial_ordering p) const
  {
    return
      p < 0 ? result_type::n_less :
      p > 0 ? result_type::n_greater :
      p == 0 ? result_type::n_equal_partial : result_type::n_unordered;
  }
};


Для вызова этой системы преобразования определил глобальные переменные as_type, as_compare
// global template object
template <typename To>
constexpr inline t_cast<To>
  as_type{};

// global object
constexpr inline t_cast<t_compare>
  as_compare{};


И добавил перегрузку оператора > (в правой части t_cast, в левой — любой тип)
Чтобы можно было использовать не только как as_type<t_compare>(var)
Но и var > as_type<t_compare>
template <typename From, typename Result>
constexpr Result
  operator > (From p_from, const t_cast<Result> p_cast)
{ return p_cast(p_from); } // вызов оператора круглые скобочки


Проверка, что это работает:
void test_switch()
{
  switch( 1 <=> 0 > as_compare )
  {
  case t_compare::n_less :
    std::cout << "less\n"; break;
    
  case t_compare::n_equal :
    std::cout << "equal\n"; break;

  case t_compare::n_greater :
    std::cout << "greater\n"; break;
  }
}

int main()
{
  t_compare v1{1.0/0.0 <=> 0.0/0.0 > as_type<t_compare>};
  t_compare v2{1.0/0.0 <=> 0.0/0.0 > as_compare};
  std::cout
    << (v1 == t_compare::n_unordered) << ' '
    << (v1 == v2) << ' '
    << (as_compare(1.0/0.0 <=> 0.0/0.0) == t_compare::n_unordered) << '\n';
  test_switch();
}


Весь код: готболт
Результат:

1 1 1
greater

Program returned: 0


Понимаю, что выбор оператора > для t_cast может быть под вопросом. Тем более что такая перегрузка возвращает не тип bool
Заглянув в доку cpp ref precedence
Видно что оператор > имеет меньший приоритет, чем <=>
Можно было бы взять bitwise оператор | вместо этого

p/s: Получилась настраиваемая система преобразований для любых типов. Которую можно использовать независимо от стандартного продвижения типов языка CPP и штук из стандартной библиотеки.

Хранить ли все ордеринги в одном енуме тоже вопрос.
Отредактировано 17.09.2023 11:21 Sm0ke . Предыдущая версия . Еще …
Отредактировано 17.09.2023 11:14 Sm0ke . Предыдущая версия .
Отредактировано 17.09.2023 11:12 Sm0ke . Предыдущая версия .
Отредактировано 17.09.2023 11:10 Sm0ke . Предыдущая версия .
Отредактировано 17.09.2023 11:09 Sm0ke . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.