[Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 08:37
Оценка: 197 (15) +1
Допустим мы хотим обернуть вызовы методов класса, что бы перед вызовом класс автоматически "блокировался", а после вызова "разблокировался". Распространенная практика.

Один подход заключается в том, что бы operator->() объекта возвращал некий прокси-объект, который в конструкторе блокирует объект, а в деструкторе разблокирует. Тут не нравится то, что прокси-объект должен быть копируемым. Т.е. надо возиться с передачей владения, и он не может содержать не копируемые вложенные объекты (boost::detail::lightweight_mutex::scoped_lock).

Второй подход заключается в том, что бы стеке руками создавать вспомогательный объект (с использованием временного объекта):
scoped_lock(x)->f();

Тут scoped_lock может (должен) быть не копируемым. Хорошо. Но тут встаёт другая проблема — тип scoped_lock должен быть свой для каждого типа пользовательского объект. Т.е. если мы хотим реализовать его в виде повторно-используемого компонента, то это будет выглядеть так:
scoped_lock<X>(x)->f();
scoped_lock<Y>(y)->g();

Не очень красиво с т.з. пользователя.


Собственно что придумалось по этому поводу.
Допустим есть 2 таких несвязанных класса:
struct X
{
    void f(){}
    boost::detail::lightweight_mutex mtx_;
};

struct Y
{
    void g(){}
    boost::detail::lightweight_mutex mtx_;
};


И мы хотим, что бы можно было писать вот так:
int main()
{
    X x;
    lock(x)->f();

    Y y;
    lock(y)->g();
}


Вот решение:

#include <boost/detail/lightweight_mutex.hpp>
#include <boost/optional.hpp>
#include <boost/utility/in_place_factory.hpp>
#include <boost/ref.hpp>

typedef boost::optional<boost::detail::lightweight_mutex::scoped_lock> lock_storage;

template<typename T>
T* lock(T& obj, lock_storage const& lock = lock_storage())
{
    const_cast<lock_storage&>(lock) = boost::in_place(boost::ref(obj.mtx_));
    return &obj;
}


Во-первых, я отделил прокси-объект от самого пользовательского объекта. Т.е. они не связаны по типам, и никто не содержит ни на кого ссылок.
Во-вторых, я отделил создание "стораджа" для прокси-объекта, от создания самого прокси-объекта. Сторадж создается на стеке в месте вызова lock(), а прокси-объект внутри функции lock(). Рушится прокси-объект в конце полного выражения в месте вызова.

По-моему, получилось достаточно интересное по свойствам решение.

При необходимости можно сделать прокси-объект шаблонным (зависимым от типа пользовательского объекта):
template<typename T>
T* lock(T& obj, lock_storage<T> const& lock = lock_storage<T>())
{
    const_cast<lock_storage<T>&>(lock) = boost::in_place(boost::ref(obj.mtx_));
    return &obj;
}

Но указывать его явно по прежнему не надо — он будет выводиться автоматически.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Call wrapper
От: Bell Россия  
Дата: 04.05.08 09:26
Оценка:
Здравствуйте, remark, Вы писали:

Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.
Любите книгу — источник знаний (с) М.Горький
Re[2]: [Trick] Call wrapper
От: Erop Россия  
Дата: 04.05.08 09:30
Оценка:
Здравствуйте, Bell, Вы писали:

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


B>Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.


Обёртку своего класса можно сделать своим другом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: [Trick] Call wrapper
От: Bell Россия  
Дата: 04.05.08 09:31
Оценка:
Здравствуйте, Erop, Вы писали:

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


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


B>>Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.


E>Обёртку своего класса можно сделать своим другом...


Если речь идет только о своих классах — то да, можно, спору нет.
Любите книгу — источник знаний (с) М.Горький
Re[4]: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 11:36
Оценка:
Здравствуйте, Bell, Вы писали:

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


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


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


B>>>Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.


E>>Обёртку своего класса можно сделать своим другом...


B>Если речь идет только о своих классах — то да, можно, спору нет.



Непосредственно открытый мьюткс с одним названием я взял для простоты изложения.
Каким-либо образом пользовательский класс всё равно должен поддерживать эту функциональность, т.к. он должен содержать мьютекс.
Более приемлемо это можно записать так:

class sync
{
  boost::mutex mtx_;
  friend ...;
};

struct X : sync
{
  // ...
};

struct Y : sync
{
  // ...
};



Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты.
И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 11:50
Оценка:
Здравствуйте, Andrew S, Вы писали:

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


R>>>Надо подумать...


R>>http://gzip.rsdn.ru/forum/message/2937445.1.aspx
Автор: remark
Дата: 04.05.08


AS>Да, спасибо, посмотрел. Интересное решение, в том плане, что кажется, будто это снимает требование наличия конструктора копирования у временного объекта. На самом деле, все остается на том же уровне — ведь scoped_lock _должен_ быть сконструирован, т.е. конструктора по умолчанию у стораджа быть просто не может.


Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.

AS>Т.е. сам подход к конструированию интересный, но он не применим в данном конкретном случае.


Именно для этого случая я его закодировал и скомпилировал.

AS>Другой вопрос касаемо этого подхода (там, где он применим) — сможет ли оптимизатор так же хорошо разобраться во всей этой кухне, как и с RVO — это надо смотреть...



Вот набросок варианта для "нелюбителей boost'а", из которого видно, что оверхеда-то тут особо и нет:

template<typename T>
struct lazy_storage
{
public:
  lazy_storage()
    : init_()
  {
  }
  
  ~lazy_storage()
  {
    if (init_)
      reinterpret_cast<T*>(buf)->~T();
  }

protected:
  union
  {
    char buf [sizeof T];
    TYPE_WITH_MAX_ALIGNMENT fake; // __int64
  };
  bool init_;
};

struct lock_storage : lazy_storage<scoped_lock>
{
  void construct(mutex& m)
  {
    assert(false == init_);
    new (buf) scoped_lock (m);
    init_ = true;
  }
};

template<typename T>
T* lock(T& obj, lock_storage const& storage = lock_storage())
{
  storage.construct(obj.mtx_);
  return &obj;
}




1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: [Trick] Call wrapper
От: Кодт Россия  
Дата: 04.05.08 12:13
Оценка: 75 (3)
Здравствуйте, remark, Вы писали:

R>Во-первых, я отделил прокси-объект от самого пользовательского объекта. Т.е. они не связаны по типам, и никто не содержит ни на кого ссылок.

R>Во-вторых, я отделил создание "стораджа" для прокси-объекта, от создания самого прокси-объекта. Сторадж создается на стеке в месте вызова lock(), а прокси-объект внутри функции lock(). Рушится прокси-объект в конце полного выражения в месте вызова.

R>По-моему, получилось достаточно интересное по свойствам решение.


Причём этот подход можно обобщить на любые ситуации с
— возвращением некопируемого временного объекта
Foo const& temp_foo( ....., Foo const& temp = Foo() ) // создаётся вызывающей стороной
{
    .....
    return temp;
}

— отложенным конструированием
Foo const& temp_foo( ....., optional<Foo> const& temp = optional<Foo>() ) // размещается вызывающей стороной,
{
    remove_const(temp) = in_place(.....); // а создаётся функцией
    return temp;
}

— созданием объектов-сателлитов
Bar apropos_foo(Bar bar, optional<Foo> const& temp = optional<Foo>() )
{
    .....
    return bar;
}

что примерно соответствует встроенному оператору ',' (только тщательно спрятанному)
(scoped_lock(x.mtx_), x).member();


Собственно, нас устроило бы вот такое
T& lock(T& obj, scoped_lock const& lk = scoped_lock(obj.mtx_))
{
  return obj;
}

Но увы, это не компилируется. Поэтому и пришлось задействовать отложенное конструирование.


Да, кстати!
Зачем сигнатура T* lock(T&) ? Только ради ->, или это плоды рефакторинга?
Если отвлечься от ->, то пускай она будет T& lock(T&), и использовать
lock(x).member();
... << RSDN@Home 1.2.0 alpha rev. 655>>
Перекуём баги на фичи!
Re[2]: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 12:57
Оценка: 23 (2)
Здравствуйте, Кодт, Вы писали:

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


R>>Во-первых, я отделил прокси-объект от самого пользовательского объекта. Т.е. они не связаны по типам, и никто не содержит ни на кого ссылок.

R>>Во-вторых, я отделил создание "стораджа" для прокси-объекта, от создания самого прокси-объекта. Сторадж создается на стеке в месте вызова lock(), а прокси-объект внутри функции lock(). Рушится прокси-объект в конце полного выражения в месте вызова.

R>>По-моему, получилось достаточно интересное по свойствам решение.


К>Причём этот подход можно обобщить на любые ситуации с

К>- возвращением некопируемого временного объекта

Вполне. Только надо учитывать, что этот объект можно использовать только "в пределах одной строки".
Для интереса можно даже так:

К>
К>Foo& temp_foo( ....., Foo const& temp = Foo() ) // создаётся вызывающей стороной
К>{
К>    .....
К>    return const_cast<Foo&>(temp);
К>}
К>


Можно ещё так:
template<size_t sz>
struct buf_t
{
  mutable char data [sz];
};

char* foo(buf_t<64> const& buf = buf_t<64>())
{
  // отъели немного стека во фрейме вызывающей функции
  // ...
  return (char*)buf;
}



О! Самое интересное так:

char* foo(void* buf = alloca(64))
{
  // отъели немного стека во фрейме вызывающей функции
  // память будет жить до конца вызывающей функции
  return strcpy((char*)buf, "Hello from dead!");
}

int main()
{
    char* str = foo();
    printf("%s", str);
    // магия: str никаким образом удалять не надо - вся строка у нас на стеке
}


Хммм... вроде это законно, т.к. вычисление аргументов по-умолчанию синтаксически есть часть вызывающей функции.
По крайней мере в MSVC никакие ран-тайм проверки целостности стека не срабатывают.


К>- отложенным конструированием

К>
К>Foo const& temp_foo( ....., optional<Foo> const& temp = optional<Foo>() ) // размещается вызывающей стороной,
К>{
К>    remove_const(temp) = in_place(.....); // а создаётся функцией
К>    return temp;
К>}
К>


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


К>- созданием объектов-сателлитов

К>
К>Bar apropos_foo(Bar bar, optional<Foo> const& temp = optional<Foo>() )
К>{
К>    .....
К>    return bar;
К>}
К>

К>что примерно соответствует встроенному оператору ',' (только тщательно спрятанному)
К>
К>(scoped_lock(x.mtx_), x).member();
К>


"(scoped_lock(x.mtx_), x)" не рулит. Изначально у меня был такой вариант "lock()(x)", где lock не шаблонный класс, но operator() у него шаблонный. Но лишние скобочки сильно смущали.


К>Собственно, нас устроило бы вот такое

К>
К>T& lock(T& obj, scoped_lock const& lk = scoped_lock(obj.mtx_))
К>{
К>  return obj;
К>}
К>

К>Но увы, это не компилируется. Поэтому и пришлось задействовать отложенное конструирование.

Ну, да. Это первое, что я написал


К>Да, кстати!

К>Зачем сигнатура T* lock(T&) ? Только ради ->, или это плоды рефакторинга?
К>Если отвлечься от ->, то пускай она будет T& lock(T&), и использовать
К>
К>lock(x).member();
К>


Да, лучше заменить на ссылку. Указатель — исторически.
Ещё одно приемущество
Переименовать lock в synchronized и прям Java получается


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: [Trick] Call wrapper
От: Erop Россия  
Дата: 04.05.08 13:42
Оценка:
Здравствуйте, remark, Вы писали:

R>Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты.

R>И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.

Есть и третий вариант, вообще-то.
Можно завести две функции, ну типа BeginLockedAccess( T& ) и EndLockedAccess( T& ), и звать из твоей функции эти враперы, для того, чтобы начать и закончить.
Ну а для разных параметров определить разные функции просто...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: [Trick] Call wrapper
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.05.08 13:43
Оценка:
R>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.

Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[3]: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 13:54
Оценка:
Здравствуйте, Andrew S, Вы писали:

R>>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.


AS>Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...



Ну твой вариант 2 — это вообще неработающий хак...
От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 13:55
Оценка:
Здравствуйте, Erop, Вы писали:

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


R>>Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты.

R>>И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.

E>Есть и третий вариант, вообще-то.

E>Можно завести две функции, ну типа BeginLockedAccess( T& ) и EndLockedAccess( T& ), и звать из твоей функции эти враперы, для того, чтобы начать и закончить.
E>Ну а для разных параметров определить разные функции просто...


Мммм... а состояние-то где хранить. Допустим BeginLockedAccess() хочет запомнить идентификатор потока, что бы EndLockedAccess() потом мог его сверить.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: [Trick] Call wrapper
От: Tonal- Россия www.promsoft.ru
Дата: 04.05.08 16:23
Оценка:
Здравствуйте, remark, Вы писали:
Таким методом можно проэмулировать динамические массивы на стеке, которые умеет gcc:
template <class T>
struct dyn_array_t {
  dyn_array_t(void* abuf, size_t n) :
    buf(static_cast<T*>(abuf)), n(n) {
    for (T* cur = buf, *end = buf + n; cur != end; ++cur)
      new(cur) T();
  }
  ~dyn_array_t() {
    for (T* cur = buf, *end = buf + n; cur != end; ++cur)
      cur->~T();
  }
  T& operator[](size_t i) {return buf[i];}
  private:
    T* buf;
    size_t n;
    dyn_array_t(const dyn_array_t&);
    const dyn_array_t& operator==(const dyn_array_t&);
};
#define init_dyn_array(T, Name, N) dyn_array_t<T> Name(alloca(sizeof(T) * N), N)

struct my_struct_t {
  int a, b;
};

void foo(size_t n) {
  init_dyn_array(my_struct_t, my, n);
  for (int i = 0; i < n; ++i)
    my[i].a = n - (my[i].b = i);
  for (int i = 0; i < n; ++i)
    printf("%d, %d", my[i].a, my[i].b);
}

int main() {
  foo(64);
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1065>>
Re[4]: [Trick] Call wrapper
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.05.08 16:25
Оценка:
R>>>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.

AS>>Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...



R>Ну твой вариант 2 — это вообще неработающий хак...


Ровно аналогичный код с boost::optional. Все там работает, ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).

R>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"


Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта. Вариант с RVO можно просто отрастить от пустой базы и присваивать константной ссылке, которая будет держать нужное время.

В общем, лично я остановился на варианте с RVO, он мне кажется более понятным как в реализации, так и по побочным эффектам...
Если интересно, могу запостить результат.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[4]: [Trick] Call wrapper
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.05.08 16:40
Оценка:
T>Таким методом можно проэмулировать динамические массивы на стеке, которые умеет gcc:

А теперь представим, что массив аллоцируют в цикле... Лучше так не эмулировать
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: [Trick] Call wrapper
От: alsemm Россия  
Дата: 04.05.08 16:49
Оценка: +1

boost::in_place(boost::ref(obj.mtx_));

Открытый член класса, да с именем, подразумевающим, что член закрыт (_ на конце) — отличное наследство несчастым, которым "повезет" сапортить этот код.
Можно конечно сделать функцию T* lock(...) другом. Но ее объявление в теле класса будет выглядеть довольно уродливо:
class A 
{
    mutex mtx_;

    friend A* lock<>(A& obj, lock_storage const&);
};


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

Алексей
Re[5]: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 17:13
Оценка:
Здравствуйте, Andrew S, Вы писали:

R>>>>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.


AS>>>Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...



R>>Ну твой вариант 2 — это вообще неработающий хак...


AS>Ровно аналогичный код с boost::optional.


Подожди-подожди. Я memcpy() объекты не копирую! У меня с т.з. языка всё законно.


R>>Все там работает,


А ты попробуй в объект добавить указатель внутрь себя. И потом погляди куда он будет указывать после memcpy() (если более точно, то там у тебя не memcpy(), а копирование встроенного в объект массива, но по сути это то же самое).


R>>ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).


Ссылка в данном случае -- указатель. На Alpha словишь аппаратное прерывание. На x86 тоже словишь, если включена аппаратная проверка выравнивания.


R>>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"


AS>Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта.


Тип стораджа *не* зависит от типа пользовательского объекта. В любом случае, что страшного в этом знании как таковом?


AS>Вариант с RVO можно просто отрастить от пустой базы и присваивать константной ссылке, которая будет держать нужное время.


AS>В общем, лично я остановился на варианте с RVO, он мне кажется более понятным как в реализации, так и по побочным эффектам...

AS>Если интересно, могу запостить результат.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: [Trick] Call wrapper
От: Bell Россия  
Дата: 04.05.08 17:32
Оценка: 1 (1)
Здравствуйте, remark, Вы писали:

R>Непосредственно открытый мьюткс с одним названием я взял для простоты изложения.

Принято.

R>Каким-либо образом пользовательский класс всё равно должен поддерживать эту функциональность, т.к. он должен содержать мьютекс.

Принято.

R>Более приемлемо это можно записать так:


R>
R>class sync
R>{
R>  boost::mutex mtx_;
R>  friend ...;
R>};

R>struct X : sync
R>{
R>  // ...
R>};

R>struct Y : sync
R>{
R>  // ...
R>};
R>



R>Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты.

R>И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.

И тем не менее, я не вижу особых преимуществ перед записью
lock<X>(x)->f();
Любите книгу — источник знаний (с) М.Горький
Re[6]: [Trick] Call wrapper
От: Andrew S Россия http://alchemy-lab.com
Дата: 04.05.08 17:37
Оценка:
R>>>Ну твой вариант 2 — это вообще неработающий хак...

AS>>Ровно аналогичный код с boost::optional.


R>Подожди-подожди. Я memcpy() объекты не копирую! У меня с т.з. языка всё законно.


Там не объект, а _ссылка_.

R>>>Все там работает,


R>А ты попробуй в объект добавить указатель внутрь себя. И потом погляди куда он будет указывать после memcpy() (если более точно, то там у тебя не memcpy(), а копирование встроенного в объект массива, но по сути это то же самое).


Так специально и сделан объект, а не ручками мувится. Все, что там надо добавить — алигн, как это сделано в бусте.

R>>>ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).


R>Ссылка в данном случае -- указатель. На Alpha словишь аппаратное прерывание. На x86 тоже словишь, если включена аппаратная проверка выравнивания.


С чего бы это вдруг ссылка стала равна указателю? В данном случае максимум, что надо, макс алигн добавить. А вообще, как я уже говорил, даже 6-ка сворачивает этот код в полностью inline, никакого там копирования нет и не будет в прицнипе Эта реализация фактически остается для совсем дубовых компиляторов.

R>>>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"


AS>>Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта.


R>Тип стораджа *не* зависит от типа пользовательского объекта. В любом случае, что страшного в этом знании как таковом?


В том, что его (знание) надо ИМЕТЬ. Что клиенту бывает довольно неудобно. Например, мне гораздо удобнее написать const Lock &tmp = lock(obj), чем пытаться вспомнить, что там за тип надо отдать в lock (смысл такой операции, кстати, клиенту может быть совсем не очевиден). Везде свои плюсы и минусы...
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[5]: [Trick] Call wrapper
От: remark Россия http://www.1024cores.net/
Дата: 04.05.08 17:38
Оценка:
Здравствуйте, Andrew S, Вы писали:

T>>Таким методом можно проэмулировать динамические массивы на стеке, которые умеет gcc:


AS>А теперь представим, что массив аллоцируют в цикле... Лучше так не эмулировать



alloca — это вообще страшная вещь.
Помимо того, что можно запросить слишком много, если не проверять размер. И помимио того, что нельзя выделять в цикле.
Самая прикольная засада то, что и такая функция может стать причиной переполнения стека:
void f()
{
  alloca(1);
}


При условии, что она вызывается так:
void g()
{
  for (int i = 0; i != 1000000; ++i)
    f();
}


И f() встроится в g(). Тогда тот самый "конец функции", в котором удаляется память, выделенная alloca(1), переносится в конец функции g(). Т.е. память выделяется, выделяется, выделяется... Всё вышеописанное относится к msvc, на gcc не проверял.






Лечится только объявлением всех функций, которые используют alloca, как __declspec(noinline).


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.