Избавится от виртуальности
От: _nn_ www.nemerleweb.com
Дата: 31.08.11 08:46
Оценка:
Пишу код для блокировок.
Придумался такой механизм:

#include <stdio.h>

class MyLock
{
public:
    MyLock() {}
    ~MyLock() {}

    void Lock()
    {
        puts(__FUNCTION__);
    }

    void Unlock()
    {
        puts(__FUNCTION__);
    }

private:
    /// @name Not copyable, not assignable
    /// @{
    MyLock(const MyLock&);
    MyLock& operator=(const MyLock&);
    /// @}
};

class ScopeLocker
{
public:
    virtual void Lock() const = 0;
    virtual void Unlock() const = 0;
};

template<typename T>
class ConcreteScopeLocker : public ScopeLocker
{
public:
    ConcreteScopeLocker(T& lock) : m_lock(lock)
    {
        Lock();
    }

    ~ConcreteScopeLocker()
    {
        Unlock();
    }

    /// @fn ConcreteScopeLocker(const ConcreteScopeLocker&)
    /// Copy constructor is implicit

    virtual void Lock() const
    {
        m_lock.Lock();
    }

    virtual void Unlock() const
    {
        m_lock.Unlock();
    }

private:
    ConcreteScopeLocker& operator=(const ConcreteScopeLocker&);

    mutable T& m_lock;
};

template<typename T>
ConcreteScopeLocker<T> LockScope(T& lock)
{
    return ConcreteScopeLocker<T>(lock);
}

int _tmain()
{
    {
        MyLock ml;
        const ScopeLocker& lockScope = LockScope(ml); // Не нужно указывать конкретный тип переменной

        int i = 1;
        puts("Inside block");

        lockScope.Unlock();


        lockScope.Lock();
    }

    return 0;
}


Однако Lock/Unlock пришлось сделать виртуальными.
В C++11 можно было бы просто сделать
auto const& lockScope = LockScope(cs);

И тогда у lockScope был бы конкретный тип.

А как можно это решить в рамках C++03 ?

P.S.
Желательно без BOOST_TYPEOF
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Избавится от виртуальности
От: uzhas Ниоткуда  
Дата: 31.08.11 09:12
Оценка: 1 (1)
Здравствуйте, _nn_, Вы писали:

__>Пишу код для блокировок.

__>Придумался такой механизм:

вообще не ясно к чему такие заморочки
Lock\Unlock по семантике должны быть неконстантными

explicit is better than implicit
class MyLockLocker : noncopyable
{
public:
  explicit MyLockLocker(MyLock& lock)
    : m_Lock(lock)
  {
    m_Lock.Lock();
  }

  ~MyLockLocker()
  {
    m_Lock.Unlock();
  }

private:
  MyLock& lock;
};

class MyLock : noncopyable
{
public:
   typedef MyLockLocker Locker;

   void Lock() {...}
   void Unlock() {...}
};

void f()
{
  MyLock sync;

  MyLock::Locker lock(sync);
  //work with data
}

void g()
{
  MyLock sync;
  MyLockLocker lock(sync);
  //work with data
}
Re: Избавится от виртуальности
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 31.08.11 09:13
Оценка:
Здравствуйте, _nn_, Вы писали:

__>В C++11 можно было бы просто сделать

__>
__>auto const& lockScope = LockScope(cs);
__>

__>И тогда у lockScope был бы конкретный тип.

__>А как можно это решить в рамках C++03 ?


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

template<typename T>
ConcreteScopeLocker<T> LockScope(T& lock)
{
    return ConcreteScopeLocker<T>(lock);
}

#define CreateLockScope(m, v) \
   const ConcreteScopeLocker<T> & v = LockScope(m)

int main()
{
    {
        MyLock ml;
        CreateLockScope(m1, lockScope); // Не нужно указывать конкретный тип переменной

        int i = 1;
        puts("Inside block");

        lockScope.Unlock();


        lockScope.Lock();
    }

    return 0;
}


Недостатки очевидны: использование препроцессора и замыливание создания lockScope (эту проблему, впрочем, легко решить немного изменив дизайн).
Re[2]: Избавится от виртуальности
От: _nn_ www.nemerleweb.com
Дата: 31.08.11 09:23
Оценка:
Здравствуйте, uzhas, Вы писали:

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


__>>Пишу код для блокировок.

__>>Придумался такой механизм:

U>вообще не ясно к чему такие заморочки

U>Lock\Unlock по семантике должны быть неконстантными

Так они и неконстантные у оригинального класса.

Как раз хочется уйти от явного указания типа.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Избавится от виртуальности
От: Chorkov Россия  
Дата: 31.08.11 09:26
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Пишу код для блокировок.


__>Однако Lock/Unlock пришлось сделать виртуальными.

__>В C++11 можно было бы просто сделать
__>
__>auto const& lockScope = LockScope(cs);
__>

__>И тогда у lockScope был бы конкретный тип.

__>А как можно это решить в рамках C++03 ?


__>P.S.

__>Желательно без BOOST_TYPEOF


Возможно нужен p-impl и универсальный ScopeLocker?
class ScopeLocker
{
    struct IImpl
    {
        virtual void Lock() = 0;
        virtual void Unlock() = 0;
        virtual ~IImpl() {}
    };
    template<class T>
    struct Impl : IImpl
    {
        T& m_lock;
        void Lock()   { m_lock.Lock(); }
        void Unlock() { m_lock.Unlock(); }
        Impl(T& l) : m_lock(l) {}
    };

    std::auto_ptr<IImpl> pImpl;

public:

    template<class T>
    ScopeLocker(T& lock) : pImpl(new Impl<T>(lock))
    {
        Lock();
    }
    ~ScopeLocker()
    {
        Unlock();
    }

    void Lock()   const { pImpl->Lock()  ; }
    void Unlock() const { pImpl->Unlock(); }

};
template<class T>
ScopeLocker LockScope(T& lock)
{
    return ScopeLocker(lock);
}
Re[2]: Пардон, это не работает (-)
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 31.08.11 09:29
Оценка:
Re[2]: Избавится от виртуальности
От: _nn_ www.nemerleweb.com
Дата: 31.08.11 09:38
Оценка:
Здравствуйте, Chorkov, Вы писали:

skip

А где тут избавление от виртуальности ?
Более того тут еще выделение памяти в куче без надобности.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Избавится от виртуальности
От: Warturtle  
Дата: 31.08.11 11:57
Оценка: :)
Здравствуйте, _nn_, Вы писали:

__>Пишу код для блокировок.

__>Придумался такой механизм:

__>...


__>И тогда у lockScope был бы конкретный тип.


__>А как можно это решить в рамках C++03 ?


__>P.S.

__>Желательно без BOOST_TYPEOF

Можно, например, на манер этого
Автор: alnsn
Дата: 02.05.04
:

#include <string.h>
#include <stdio.h>
#include <typeinfo.h>

class MyLock
{
public:
    MyLock() {}
    ~MyLock() {}

    void Lock()
    {
        puts(__FUNCTION__);
    }

    void Unlock()
    {
        puts(__FUNCTION__);
    }

private:
    /// @name Not copyable, not assignable
    /// @{
    MyLock(const MyLock&);
    MyLock& operator=(const MyLock&);
    /// @}
};

class ScopeLockerBase
{
    // forward declarations
    template< class T >    struct Functions;

public: 
    // interface
    template< class T >
    ScopeLockerBase(T & x)
        : m_p(&x)
        , m_t(&Functions< T >::s_table)
    {
        //printf("ScopeLockerBase( \"%s\" )\n", typeid(T).name());
    }
    ~ScopeLockerBase()
    {
        //printf("~ScopeLockerBase\n");
    }

    void Lock() const
    {
        //printf("ScopeLockerBase::Lock - dispatch\n");
        m_t->Lock(const_cast< void * >(m_p));
    }
        
    void Unlock() const
    {
        //printf("ScopeLockerBase::Unlock - dispatch\n");
        m_t->Unlock(const_cast< void * >(m_p));
    }
    
private:
    // Function table type for the Bazo interface
    struct Table
    {
        void (*Lock)(void *);
        void (*Unlock)(void *);
    };

    // For a given referenced type T, generates functions for the
    // function table and a static instance of the table.
    template< class T >
    struct Functions
    {
        static ScopeLockerBase::Table const s_table;
        
        static void Lock(void * p)
        {
            static_cast< T * >(p)->Lock();
        }
    
        static void Unlock(void * p)
        {
            static_cast< T * >(p)->Unlock();
        }
    };
    
    void const * m_p;
    Table const * m_t;
};

template< class T >
ScopeLockerBase::Table const ScopeLockerBase::Functions< T >::s_table = { 
    &ScopeLockerBase::Functions< T >::Lock
    , &ScopeLockerBase::Functions< T >::Unlock
};

struct ScopeLockerAuto : public ScopeLockerBase
{
    template< class T >
    ScopeLockerAuto(T & x)
        : ScopeLockerBase(x)
    {
        this->Lock();
    }
    ~ScopeLockerAuto()
    {
        this->Unlock();
    }
};

template<typename T>
ScopeLockerAuto LockScope(T & lock)
{
    return ScopeLockerAuto(lock);
}


int main()
{
    {
        MyLock ml;
        const ScopeLockerAuto& lockScope = LockScope(ml); // Не нужно указывать конкретный тип переменной

        int i = 1;
        puts("Inside block");

        lockScope.Unlock();

        lockScope.Lock();
    }
}
Re[2]: Избавится от виртуальности
От: Warturtle  
Дата: 31.08.11 12:01
Оценка:
Здравствуйте, Warturtle, Вы писали:

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


__>>Пишу код для блокировок.

__>>Придумался такой механизм:

__>>...


__>>И тогда у lockScope был бы конкретный тип.


__>>А как можно это решить в рамках C++03 ?


__>>P.S.

__>>Желательно без BOOST_TYPEOF

W>Можно, например, на манер этого
Автор: alnsn
Дата: 02.05.04
:


W>...

Причем функции Lock/Unlock в MyLock могут быть и статическими (сомнительный бонус, конечно, но все-таки=).
Re[2]: Избавится от виртуальности
От: Alexander Poluektov Германия http://www.google.com/profiles/alexander.poluektov#buzz
Дата: 31.08.11 12:03
Оценка:
Здравствуйте, Warturtle, Вы писали:

W> void Lock() const

W> {
W> //printf("ScopeLockerBase::Lock — dispatch\n");
m_t->>Lock(const_cast< void * >(m_p));
W> }

W> void Unlock() const

W> {
W> //printf("ScopeLockerBase::Unlock — dispatch\n");
m_t->>Unlock(const_cast< void * >(m_p));
W> }

W>private:

W> // Function table type for the Bazo interface
W> struct Table
W> {
W> void (*Lock)(void *);
W> void (*Unlock)(void *);
W> };
W>[/ccode]

И правда, слова virtual больше нигде нет -- потому что теперь закат Солнца выполняется вручную.
Re: А зачем избавляются?
От: Alex Dav Россия  
Дата: 31.08.11 14:16
Оценка: :)
просто для повышения моих знаний — расскажите плиз зачем это надо.
Спасибо.
Re[2]: А зачем избавляются?
От: _nn_ www.nemerleweb.com
Дата: 31.08.11 15:00
Оценка:
Здравствуйте, Alex Dav, Вы писали:

AD>просто для повышения моих знаний — расскажите плиз зачем это надо.

AD>Спасибо.

Просто так избавляться не обязательно.
Это оптимизация, к которой нужно подходить внимательно.
( Если конечно виртуальная функция используется не по делу и тогда это пессимизация )

Без виртуальной функции компилятор сможет соптимизировать вызов и даже заинлайнить код.
С виртуальной функцией вероятность этого гораздо ниже.

Кроме того в некоторых контекстах нельзя использовать виртуальные функции вообще из-за невозможности контроля над таблицой виртуальных функций.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: А зачем избавляются?
От: Alex Dav Россия  
Дата: 31.08.11 15:08
Оценка:
Здравствуйте, _nn_, Вы писали:
...

Спасибо
Re: Избавится от виртуальности
От: Alexander G Украина  
Дата: 31.08.11 15:56
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Пишу код для блокировок.

__>Придумался такой механизм:


template<typename T>
class ConcreteScopeLocker : public ScopeLocker
{
public:
    ConcreteScopeLocker(T& lock) : m_lock(lock)
    {
        Lock();
    }

    ~ConcreteScopeLocker()
    {
        Unlock();
    }

    /// @fn ConcreteScopeLocker(const ConcreteScopeLocker&)
    /// Copy constructor is implicit


Плохой механизм, если ConcreteScopeLocker коснтруктор копий будет вызван, анлоков будет больше чем локов.
http://codepad.org/ntHR4daV
Русский военный корабль идёт ко дну!
Re: Избавится от виртуальности
От: rg45 СССР  
Дата: 31.08.11 16:01
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Пишу код для блокировок.

__>Придумался такой механизм:
__>...

__>Однако Lock/Unlock пришлось сделать виртуальными.

__>В C++11 можно было бы просто сделать
__>
__>auto const& lockScope = LockScope(cs);
__>

__>И тогда у lockScope был бы конкретный тип.

__>А как можно это решить в рамках C++03 ?


__>P.S.

__>Желательно без BOOST_TYPEOF


По-моему, все можно упростить. Схематично так (не компилировал):
#include <boost/shared_ptr.hpp>
#include <boost/noncopyable.hpp>

template<typename T>  
void _unlock(T* t) { t->Unlock(); }

class ScopeLocker : boost::noncopyable
{
public:  
  
template<typename T>
explicit ScopeLocker(T& my_lock) : m(&my_lock, &_unlock<T>) { }

private:
  boost::shared_ptr<void> m;
};


Unlocker делается аналогично.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: Избавится от виртуальности
От: _nn_ www.nemerleweb.com
Дата: 31.08.11 16:15
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Плохой механизм, если ConcreteScopeLocker коснтруктор копий будет вызван, анлоков будет больше чем локов.

AG>http://codepad.org/ntHR4daV

Знаем. Это лечится через move constructor или его аналога
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Избавится от виртуальности
От: _nn_ www.nemerleweb.com
Дата: 31.08.11 16:17
Оценка:
Здравствуйте, rg45, Вы писали:

Оверкил какой-то.
Разве shared_ptr не выделяет память для счетчика ?

Проще тип уже написать
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[3]: Избавится от виртуальности
От: Alexander G Украина  
Дата: 31.08.11 19:00
Оценка:
Здравствуйте, _nn_, Вы писали:

__>Знаем. Это лечится через move constructor или его аналога


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

я вообще думаю, что в обычном юз кейсе для лока не нужно вызывать .Unlock и .Lock .
в конструкторе/деструкторе же вызовы и так невиртуальны (если компилятор не додумается их сделать невиртуальными, ему можно помочь.
Русский военный корабль идёт ко дну!
Re: Избавится от виртуальности
От: Andrew S Россия http://alchemy-lab.com
Дата: 31.08.11 19:20
Оценка:
__>Однако Lock/Unlock пришлось сделать виртуальными.
__>В C++11 можно было бы просто сделать
__>
__>auto const& lockScope = LockScope(cs);
__>

__>И тогда у lockScope был бы конкретный тип.

__>А как можно это решить в рамках C++03 ?


В поставленном ключе, на мой взгляд, задача легальным способом силами С++ 03 не решается. Все сводится к необходимости изобрести свой TYPEOF. Что обычно имеет тенденцию не работать на различных полуэкзотических компиляторах.

Лучше сделать как в бусте. Тайпдефом — LockObject::ScopedLock.
На первый взгляд это выглядит не сильно лучше ScopedLock<LockObject>, но на деле позволяет писать трейты, не задумываясь о пригодности ScopedLock для данного объекта.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: Избавится от виртуальности
От: rm822 Россия  
Дата: 01.09.11 05:22
Оценка:
см http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx

class ScopeLocker
{
    template<typename U> void Lock()   { ((U*)m_Lock)->Lock(); }
    template<typename U> void Unlock() { ((U*)m_Lock)->Unlock(); }
    typedef void (ScopeLocker::*PFN)(void);
    void* m_Lock;
    PFN fnLock;
    PFM fnUnlock;
    
public:
    template<typename T>
    ScopeLocker(T& lock) : m_lock(&lock), fnLock(&ScopeLocker::Lock<T>),fnUnlock(&ScopeLocker::Unlock<T>)
    {
        Lock();
    }
    ~ScopeLocker()
    {
        Unlock();
    }

    void Lock()
    {
        (this->*fnLock)();
    }

    void Unlock()
    {
        (this->*fnUnlock)();
    }
};


с точки зрения произодительности это все дохлый номер
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.