LINUX.ORG.RU

Какое же говнище этот ваш С++

 


11

7

Решил намедни углубить свои знания по плюсам, чувствуя, что скоро нехило так потребуются по работе. Теперь сижу, обмазываюсь тут всякими трупами страусов, Скоттом Майерсом и другими. Г-пди, как же можно на этом писать, особенно после знания божественных лиспов, хаскелей и прочих матанских агд (sic!). Это какая-то пытка, честное слово, мне натурально мерзко и противно читать как люди пытаются вырезать гланды через задний проход да ещё и хвалятся этим, поглядите, мол, как это круто. Такое ощущение, будто плюсисты все поголовно латентные мазохисты.

template <typename T>
class Rational
{
    public:
    ...
    friend const Rational operator*(const Rational& lhs, const Rational& rhs)
    {
        return Rational(lhs.numerator() * rhs.numerator(), // same impl
            lhs.denominator() * rhs.denominator()); // as in Item 24
    }
}

An interesting observation about this technique is that the use of friendship has nothing to do with a need to access non-public parts of the class. In order to make type conversions possible on all arguments, we need a non-member function (Item 24 still applies); and in order to have the proper function automatically instantiated, we need to declare the function inside the class. The only way to declare a non-member function inside a class is to make it a friend. So that's what we do. Unconventional? Yes. Effective? Without a doubt.

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

Перемещено mono из talks

★★★★★

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

если честно, то я не понял где оно надо, и как собрать, чтобы поиграться с этим подробнее.

Нормальные как раз в С++ :) Т.е. индексация любыми числами, enum-ами и вообще объектами (если делать ссылки на constexpr).

нормальные относительно текущих решений в haskell, там нормальный констрейнт солвер будет.

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

если честно, то я не понял где оно надо, и как собрать, чтобы поиграться с этим подробнее.

Ну собрать c++ -std=c++11 file-name.cc && ./a.out — оно должно написать, что sizeof(short) для a и sizeof(int) для b и c, то есть decltype для auto c выберет типом S<B>, то есть из A (~ short) и B (~ int) в качестве sup возьмёт B, S<B> c2 скомпилируется, S<A> c3 — нет, в итоге при сложении матриц с элементами разного типа результирующая матрица всегда будет иметь типом элемента тип который достаточен (sup) для отсутствия переполнения (кроме своего собственного).

нормальные относительно текущих решений в haskell

haskell

*GHC. Надеюсь, будет так же естественно как и std::array и т.п.

Индексация по числовому типу:

// concat : {T : Type, a b : Size} -> Array T a -> Array T b -> Array T (a + b)
template <typename T, size_t a, size_t b>
std::array<T, a + b> concat(std::array<T, a> const& x, std::array<T, b> const& y) {
    std::array<T, a + b> z;
    std::copy(x.begin(), x.end(), z.begin());
    std::copy(y.begin(), y.end(), z.begin() + x.size());
    return z;
}

    std::array<int, 3> x = {{1, 2, 3}};
    std::array<int, 2> y = {{4, 5}};
    auto z = concat(x, y); // std::array<int, 5>

По enum-у:

enum class abc { a = 1, b, c };

// foo : {abc} -> R
template <abc> R foo();
template <> R foo<abc::a>() { ... }
template <abc bc> R foo() { ... bc ... }

По числовому типу и объекту-массиву с зависимостью в сигнатуре:

// bar {rank : Size, sizes : Array Size rank} : Type
template<size_t rank, std::array<size_t, rank>& sizes>
struct bar {
    ...
};

extern std::array<size_t, 3> s;
std::array<size_t, 3> s = {{1, 2, 3}};

    bar<3, s> a;

хотя тут уже немного криво, так как нужно обеспечивать постоянство адреса s.

Пока я не знаю как такие вещи нормально в хаскеле делать.

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

Вот такой аналог придумался:

{-# LANGUAGE MultiParamTypeClasses, TypeFamilies #-}

class Mono a b where
  mono :: a -> b

instance Mono Int Integer where
  mono = toInteger

instance Mono Integer Integer where
  mono = id

-- ... over100500 instances

type family Sup a b :: *

type instance Sup Int Integer = Integer

-- ... again

data Matrix a = Matrix a

(<+>) :: (Num c, c ~ Sup a b, Mono a c, Mono b c) => Matrix a -> Matrix b -> Matrix c
Matrix x <+> Matrix y = Matrix (mono x + mono y)

-- Matrix (1 :: Int) <+> Matrix (2 :: Integer) :: Matrix Integer

vs

template <typename A>
struct Matrix {
    A value;
    /* explicit? */ Matrix(A const& value_) : value(value_) {}
    template<class B>
    friend Matrix<decltype(A() + B())> operator+(Matrix const& x, Matrix<B> const& y) {
        return /* no conv? */ x.value + y.value;
    }
};

    auto x = Matrix<int>(1) + Matrix<double>(2);
    // ^ Matrix<double>

за счёт того что operator+ не мономорфный и берёт на себя проведение вложений (в лучшем случае, конечно).

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

да сложновато, но так ли оно надо. Я бы делал так:

{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances #-}

import Data.Word

class Up a b where up :: a -> b

-- cheating
instance (Integral a, Integral b) => Up a b where up = fromIntegral 

type family  Sup a b :: *
type instance Sup Word8 Word16 = Word16
type instance Sup Word8 Word32 = Word32
-- 100500 штук, но семейство открытое, можно доопределять или генерить TH

liftS :: (Up a c) => a -> c
liftS = up

liftS2 :: (Up a c1, Up b c2) => (c1 -> c2 -> d) -> a -> b -> d
liftS2 f a b = f (up a) (up b)

проверка:

λ> :t liftS2 (+) (1::Word8) (2::Word16) :: Word32
liftS2 (+) (1::Word8) (2::Word16) :: Word32 :: Word32
λ> :t liftS2 (+) (1::Word8) (2::Word16) 
liftS2 (+) (1::Word8) (2::Word16) :: Integral d => d
λ> liftS2 (+) (1::Word8) (2::Word16) 
3
λ> let k = liftS2 (+) (1::Word8) (2::Word16) -- дефолтный тип
λ> :t k
k :: Integer

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

А зачем Sup не используется?

instance (Integral a, Integral b) => Up a b where up = fromIntegral

*Main> up (2^63 - 1 + 1 :: Integer) :: Int
-9223372036854775808

плохие инстансы затесались, то есть не вложения. Плавающие числа не работают, как и прочие вещи (... => Up a b не оставляет возможности настраивать инстансы). На самом деле (+) будет работать через Integer (или через то что в default у Num — -Wall скажет что «Defaulting the following constraint(s) to type `...'» и в core видно). Точный sup для последующего использования с (+) не выбирается.

Идея с liftS2:

-- http://en.wikipedia.org/wiki/Monomorphism, http://ncatlab.org/nlab/show/subobject
class a :>-> b where
  embed :: a -> b

instance Word8 :>-> Word16 where
  embed = fromIntegral

instance Word16 :>-> Word16 where
  embed = id

-- http://en.wikipedia.org/wiki/Join_and_meet, http://en.wikipedia.org/wiki/Pushout_(category_theory)
type family a :\/ b :: *
type instance Word8 :\/ Word16 = Word16

liftS2 :: (a :>-> c, b :>-> c, c ~ (a :\/ b)) -- O_o
       => (c -> c -> d) -> a -> b -> d
liftS2 f a b = f (embed a) (embed b)

так Word-ы, Int-ы и выбирается ровно sup, то есть Word16, на нём работает (+):

*Main> :t liftS2 (+) (1 :: Word8) (2 :: Word16) 
liftS2 (+) (1 :: Word8) (2 :: Word16) :: Word16

но так ли оно надо

Вообще да, но из всего этого я бы хотел стандартного класса (:>->) — с только тотальными инстансами, ценен не сам он, а эти инстансы, то есть единожды сделанные эффективные безопасные преобразования (= вложения) для чего угодно. Вот в C++ безопасные поднятия производятся вообще без кастов, отсюда необходимость городить там такие вещи.

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

прикольно, заодно и на roles ссылка, о которых я ещё не читал. Ну и смысл safeCoerce я понимаю, в отличии от автоматического кастования в Sup :)

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

спать поря мне было, ща поробую сделать поаккуратнее свой вариант.

там тупо Sup a b ~ d было забыто, хотя я не уверен, что в сложных случаях настолько полиморфная функция выведется и придётся прокси делать. Если заменить на liftS2 :: (Sup a b ~ с,Up a c, Up b c) => (c -> c -> d) -> a -> b -> d, то будет проще.

По поводу up, скорее всего этот класс не стоит экспортировать и использовать явно, т.к. если он спрятан за Sup, то всё должно быть хорошо.

А так ещё круче:

{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}

import Data.Word

class Up a b where 
  up :: a -> b

-- cheating
instance (Sup a b ~ b,Integral a, Integral b) => Up a b where up = fromIntegral 

проверка

λ> up (2^63 - 1 + 1 :: Integer) :: Int

<interactive>:13:1:
    Couldn't match type `Sup Integer Int' with `Int'

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

в отличии от автоматического кастования в Sup

Ну в хаскеле скорее всего будет просто instance AdditiveGroup a => AdditiveGroup (Matrix a), то есть (^+^) :: AdditiveGroup a => Matrix a -> Matrix a -> Matrix a, как и у (+) в Num — они общеприняты мономорфными, так как нет subtype polymorphism — «аналог» не нужен.

В C++ есть ООП, то есть subtype polymorphism для примитивных типов, классов и указателей с ссылками, но с parametricity он не заводится — нет ковариантности, то есть C[+T], T <: U | C[T] <: C[U]; T' <: T, U <: U' | T -> U <: T' < U' и т.п. Поэтому приходится делать разные манипуляции в типах у шаблонов (которые отвечают за parametricity в плюсах).

А где-то в Scala есть ковариантность, поэтому там такие манипуляции не нужны. Только ценой того, что, например, «массив структур» не может упаковать в память сами структуры — только ссылки на них (поэтому же std::vector<B>, при B <: A не сможет чудесным образом подняться в std::vector<A>, так как расположение в памяти другое).

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

instance (Sup a b ~ b,Integral a, Integral b) => Up a b where up = fromIntegral

Так да (хотя и cheating, но мы всё равно не знаем нафига оно нужно :)), если дописать все Sup то будет «Couldn't match type `Integer' with `Int'».

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

Только ценой того, что, например, «массив структур» не может упаковать в память сами структуры — только ссылки на них

Хотя массивы там и не ковариантны, так что никаких проблем.

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