LINUX.ORG.RU

Свой класс Any

 


0

1

Помогите разобраться с заданием:

В первом уроке вы реализовали простой шаблон ValueHolder, в этом задании мы используем его чтобы написать класс Any (интересно, что не шаблонный), который позволяет хранить значения любого типа! Например, вы сможете создать массив объектов типа Any, и сохранять в них int-ы, double-ы или даже объекты Array. Подробности в шаблоне кода. Hint: в нешаблонном классе Any могут быть шаблонные методы, например, шаблонный конструктор.

// Эти классы реализовывать заново не нужно
struct ICloneable;

// Поле data_ типа T в классе ValueHolder
// открыто, к нему можно обращаться
template <typename T>
struct ValueHolder;

// Это класс, который вам нужно реализовать
class Any
{
    // В классе Any должен быть конструктор,
    // который можно вызвать без параметров,
    // чтобы работал следующий код:
    //    Any empty; // empty ничего не хранит

    // В классе Any должен быть шаблонный
    // конструктор от одного параметра, чтобы
    // можно было создавать объекты типа Any,
    // например, следующим образом:
    //    Any i(10); // i хранит значение 10

    // Не забудьте про деструктор. Все выделенные
    // ресурсы нужно освободить.

    // В классе Any также должен быть конструктор
    // копирования (вам поможет метод clone
    // интерфейса ICloneable)

    // В классе должен быть оператор присваивания и/или
    // шаблонный оператор присваивания, чтобы работал
    // следующий код:
    //    Any copy(i); // copy хранит 10, как и i
    //    empty = copy; // empty хранит 10, как и copy
    //    empty = 0; // а теперь empty хранит 0

    // Ну и наконец, мы хотим уметь получать хранимое
    // значение, для этого определите в классе Any
    // шаблонный метод cast, который возвращает
    // указатель на хранимое значение, или нулевой
    // указатель в случае несоответствия типов или
    // если объект Any ничего не хранит:
    //    int *iptr = i.cast<int>(); // *iptr == 10
    //    char *cptr = i.cast<char>(); // cptr == 0,
    //        // потому что i хранит int, а не char
    //    Any empty2;
    //    int *p = empty2.cast<int>(); // p == 0
    // При реализации используйте dynamic_cast,
    // который мы уже обсуждали ранее.
};

Вот мое решение:

#include <iostream>

using namespace std;

struct ICloneable
{
    virtual ICloneable* clone() const = 0;
    virtual ~ICloneable() { }
};

template <typename T>
struct ValueHolder : ICloneable {
    ValueHolder(const T& data): data_(data){}
    T data_;
    ValueHolder * clone() const {
        return new ValueHolder(*this);
    }
};

class Any
{
    ICloneable * ptr;

public:
    Any() : ptr(0) { }

    template <class value_t>
    Any(const value_t& v_) : ptr(new ValueHolder<value_t>(v_)) { }

    Any(Any const & other) : ptr(other.ptr ? other.ptr->clone() : 0) {}

    Any& operator=(Any const & other)
    {
        if(this->ptr)
            delete this->ptr;
        this->ptr = NULL;
        if (other.ptr)
        {
            this->ptr=other.ptr->clone();
        }
        return *this;
    }
    template <class A>
    Any& operator=(A const& other)
    {
        if(this->ptr)
            delete this->ptr;
        this->ptr = NULL;
        if (other)
        {
            this->ptr=new ValueHolder<A>(other);
        }
        return *this;
    }

    ~Any() { delete this->ptr; }
    template <class T>
    T* cast()
    {
        if (dynamic_cast<ValueHolder<T>*>(this->ptr))
        {
            return (T*)dynamic_cast<ValueHolder<T>*>(this->ptr);
        }
        else
        {
            return 0;
        }
    }
};


int main()
{

    Any empty;
    Any i(10);
    cout << "[1] i=" << i.cast<int>() << endl;
    Any copy(i);
    cout << "[2] copy=" << copy.cast<int>() << endl;
    empty = copy;
    cout << "[3] empty=" << empty.cast<int>() << endl;
    empty = 0;
    cout << "[4] empty=" << empty.cast<int>() << endl;
    int *iptr = i.cast<int>();
    cout << "[5] *iptr=" << iptr << endl;
    char *cptr = i.cast<char>();
    cout << "[6] *cptr=" << cptr << endl;
    Any empty2;
    int *p = empty2.cast<int>();
    cout << "[7] *p=" << p << endl;
    Any a = 20;
    cout << "[8] a=" << a.cast<int>() << endl;
    a=0;
    cout << "[9] a=" << a.cast<int>() << endl;
    a='w';
    cout << "[10] a=" << a.cast<char>() << endl;
    return 0;
}

Засада в методе cast и [6] тесте. Как мне вернуть нулевой указатель при несоответствии типов char *cptr = i.cast<char>()?

★★

чувак, ну нафик ты пошёл на степик? что бы на лоре постов про плюсы побольше было?:)

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

На степике не удобно общаться. А тут столько профессионалов, никогда без помощи не останешься.

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

да речь не о том, не проще разве просто подумать.

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

pon4ik ★★★★★
()

return (T*)dynamic_cast<ValueHolder<T>*>(this->ptr);

Но зачем сначала делать нормальный плюсовый dynamic_cast, а затем его результат кастить в сишном стиле?

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

Чем больше знакомлюсь с плюсами, тем больше понимаю почему в них все плюют, почему так популярны С# и Java, почему крупнейшие IT компании хотят создать замену плюсам, и на сколько крут Н.Вирт у которого каждый последующий язык был проще предыдущего. И хочется еще раз перечитать SICP и помучить Scheme.

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

Просто многие их суют не туда

этот топик - классический пример сования не туда. Ненужный класс со стертым типом, а потом использовать dynamic_cast в рантайме. Это ненужно на ненужно поверх ненужно.

Progressive
()

Это пц. Каким надо быть упоротым, что бы предпочесть привидённый код простому юниону?

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

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

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

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

Если более конкретно - например биржевое, и некоторые виды банковского ПО, системы хранения данных, операционные системы в конце концов, хм, вообщем если подумать список может получиться норм такой.

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

То есть учится нужно только тогда когда уже все умеешь? Ответ очевиден - чтобы шарить.

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

Ну может и можно, только вот непонятно на чём делать, хм. Можно конечно на ансисишечке, но часто больно много букоф выходит.

При хорошей архитектуре это не страшно, но гдеж нынче сыщешь хорошую.

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

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

Каким надо быть упоротым, что бы предпочесть привидённый код простому юниону?

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

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

А для как задач нужна латентность в наносекундах? Не лучше ли запилить на плисе? У меня даже пример есть, лежит на соседнем столе. А на спп такое делать? Мне даже в голову не приходит зачем.

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

Т.е. ты пишешь километры кода вместо пары строк лишь для того, что бы гипотетически пробросить его в дотнет? Весьма прагматичный подход, согласен.

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

Ну например, если ты делаешь апи, гипотетичность резко спадает.

А для как задач нужна латентность в наносекундах.

Как вариант парсеры бинарных протоколов.

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

если ты делаешь апи

апи на спп? и ты тут же пишешь про наносекунды? ты не находишь это странным?

Как вариант парсеры бинарных протоколов.

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

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

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

апи на спп? и ты тут же пишешь про наносекунды? ты не находишь это странным?

ты не поверишь :)

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

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

Кто тебе сказал такую херню? Не уж-то сам придумал, да ещё и в свой высер поверил.

Ну давай, расскажи мне за счет чего «на плюсах» будет короче.

Ты же мне конечно же ещё покажешь примеры этого самого короче. Причем код должен делать что-то реальное и полезное, а не высосанное из пальца говно.

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

но короче

Конечно короче, ведь return в конце не надо писать! А simd инструкций как было N штук и так и осталось. Или ты знаешь как их количество сделать меньше за счёт эксепшенов и raii?

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

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

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

Как же ты эпично и жеденько обделался.

Привести пример своего «короче»/«проще» осилишь, либо так же обосрёшься?

anonymous
()

как вариант можно еще хранить type_info и в методе cast использовать static_cast, если типы совпадают

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

анонимусом каждый может, учись так, это интереснее

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

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

А, и это, прозводительность, да! Это вообще опупенный аргумент. „Мы можем в большие системы (ведь спп для больших систем?), а производительность как на с“, - говорять плюсовики. „Ну тогда у вас будут те же проблемы, что у и С“, - отвечают им. „А вот нифига! У нас есть умные указатели и stl!“ - кричат сплюсники. „Так они же с оверхедом не?“, - задают им вопрос Сишники. Но сплюсники не отвечают, они начинают думать о том, как бы сделать так, что бы спп наконец-то появились треды и поддержка файловой системы. Прямо праздник. А большая система получается почему-то тормозная как кде.

Ну вобщем такие дела, да.

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

Т.е. ответить тебе нечего?

Эти детсадовские отмазончики. Тыж высрал какое-то утверждение, но не можешь его аргументировать - кто ты после этого? Правильно, базарная балаболка. Все утверждения которой основаны на «бабка за углом сказала» и естественно, что аргументов никаких не будет априори.

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

Большие системы получаются норм, если их делает не толпа, не сильно замотивированных студентов :)

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

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

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

сдается мне ты с какими то злобными, и глупыми цппэшниками просто общаешься :)

Возможно я выразился не точно, попробую перефразировать:

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

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

Или парсер потокола аля FAST.

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

сдается мне ты с какими то злобными, и глупыми цппэшниками просто общаешься :)

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

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

гы, лол.

Веб на крестах это некий особый вид изващения. Балансер для высоконагруженного рест сервиса ещё можно представить, и то зачем, когда есть nginx. Видимо моё сознание фильтрует подобные треды, хм.

pon4ik ★★★★★
()

Почти доделал этот чёртов класс, осталось ошибку найти. Может кто поможет?

#include <iostream>
#include <typeinfo>

using namespace std;

struct ICloneable
{
    virtual ICloneable* clone() const = 0;
    virtual ~ICloneable() { }
};

template <typename T>
struct ValueHolder : ICloneable {
    ValueHolder(const T& data): data_(data){}
    T data_;
    ValueHolder * clone() const {
        return new ValueHolder(*this);
    }
};

class Any
{
    ICloneable * ptr;

public:
    Any() : ptr(0) { }
    template <class V>
    Any(const V& v) : ptr(new ValueHolder<V>(v)) { }
    Any(Any const & other) : ptr(other.ptr ? other.ptr->clone() : 0) {}
    Any& operator=(Any const & other)
    {
        delete ptr;
        ptr = 0;
        if (other.ptr)
        {
            ptr = other.ptr->clone();
        }
        return *this;
    }
    template <class A>
    Any& operator=(A const& other)
    {
        delete ptr;
        ptr = 0;
        ptr = new ValueHolder<A>(other);
        return *this;
    }
    ~Any() { delete ptr; }
    template <class T>
    T* cast()
    {
        ValueHolder<T> * vh = dynamic_cast<ValueHolder<T>*>(ptr);
        if (!vh)
            return 0;
        return &(vh->data_);
    }
};


int main()
{
    Any empty;
    Any i(10);
    cout << "[1] i      = " << i.cast<int>() << endl;
    Any copy(i);
    cout << "[2] copy   = " << copy.cast<int>() << endl;
    empty = copy;
    cout << "[3] empty  = " << empty.cast<int>() << endl;
    cout << "[3] *empty = " << *empty.cast<int>() << endl;
    empty = 0;
    cout << "[4] empty  = " << empty.cast<int>() << endl;
    cout << "[4] *empty = " << *empty.cast<int>() << endl;
    int *iptr = i.cast<int>();
    cout << "[5] iptr   = " << iptr << endl;
    char *cptr = i.cast<char>(); // cptr = 0
    // cout << "[6] cptr=" << cptr << endl; // undefined behavior for char * == 0
    cout << "[6] cptr   = " << (void*)cptr << endl;
    Any empty2;
    int *p = empty2.cast<int>();
    cout << "[7] p      = " << p << endl;
    Any a = 20;
    cout << "[8] a      = " << a.cast<int>() << endl;
    a=0;
    cout << "[9] a      = " << a.cast<int>() << endl;
    a = 'w';
    cout << "[10] a     = " << a.cast<char>() << endl; // overloaded operator << for char *
    cout << "[10] a     = " << (void *)a.cast<char>() << endl;
    return 0;
}

В cast происходит разименование невалидного указателя, как его отловить?

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

1. в операторе присваивания проверку на самого себя 2. в T* cast() проверку ptr на NULL перед dynamic_cast

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

Дружище, а не мог бы ты в экстренном порядке на меня как-то выйти? А то на степике осталась буквально пара заданий, а голова уже совсем не варит. Если не тяжело напиши мне в vk.com (vk.com/uchaly) или в мордокниге (facebook.com/alexus.nalog). Буду очень признателен.

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