- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
template <typename T>
struct canref {
struct yes { uint8_t bytes[1]; };
struct no { uint8_t bytes[2]; };
template <typename U> static yes test(U*p);
template <typename U> static no test(...);
static const bool value = sizeof(test<T>(NULL)) == sizeof(yes);
};
template <typename T, int N, bool CanRef=canref<T>::value>
class array;
// class for all types
template <typename T, int N>
class array <T,N,true>
{
struct Bytes
{
uint8_t bytes[sizeof(T)];
};
struct TStruct
{
T t;
TStruct(T t):t(t){}
};
Bytes elements[N];
int count;
void copy_ctor (int index, const T& t)
{
new (&((*this)[index])) T(t);
}
void copy (int index, const T& t)
{
(*this)[index] = t;
}
void dtor (int index)
{
(*this)[index].~T();
}
public:
array () : count(0) {}
~array ()
{
resize(0);
}
T& operator [] (int index)
{
assert (index>=0 && index<count);
return ((TStruct*)(&elements[index]))->t;
}
const T& operator [] (int index) const
{
assert (index>=0 && index<count);
return ((TStruct*)(&elements[index]))->t;
}
template <int M>
array (const array<T,M> &a)
{
assert(a.count<=N);
count = a.count;
for (int i=0; i<count; ++i)
copyctor(i, a[i]);
}
template <int M>
array& operator = (const array<T,M> &a)
{
if (this != &a)
{
if (count>a.count)
{
for (int i=0; i<a.count; ++i) copy(i, a[i]);
for (int i=a.count; i<count; ++i) dtor(i);
count = a.count;
} else
{
assert(a.count<=N);
int ocount = count;
count = a.count;
for (int i=0; i<ocount; ++i) copy(i, a[i]);
for (int i=ocount; i<count; ++i) copy_ctor(i, a[i]);
}
}
}
int size()
{
return count;
}
Я тут как-то днесь код читал для 7.7, написанный человеком, не могущим в "алгоритмы". Если бы не сила воли, я бы лоб себе разбил фейспальмами.
после него 1с - мана небесная
[color]LispGovno[/color]
class raw_storage_iterator
чтобе передать в OutputIt не указатель, а например другой итератор?
Может быть вот так его юзают?
Ну как то так.
Это приведет к вызову конструктора копирования в неинициализированные области памяти.
(компилировать не пробовал, но все логично)
Ты пишешь игру в сто строк? Тогда тебе перевод каретки нужно вставлять реже.
Самое сложное - инклуды. А остальное и в одну войдет.
- в конструкторе передать нельзя
- скобки крашатся, если index >= count, а он всегда равен 0
- конструктор копий и присваивание требуют другой такой же массив
Вещь в себе какая-то ;(
push_back
он не влез в 100 строк
а ещё там вторая реализация класса для случая, когда третий параметр false
УУУУУИИИИИХХХХААААААААА!!!!
Моё шаблонное говно выложил не я, а другой человек.
Ваш индекс научного цитирования увеличился вдвое.
Верхняя сама смещается в push_back
Ещё есть resize, который умеет только уменьшать, иначе надо чем-то заполнять добавленный хвост, а чем, ведь мой класс не требует пустого конструктора от T.
Тарас, объясни, зачем нужен TStruct?
Сейчас он не нужен.
Что такое паддинг? Если чё, я специально спрашивал, смещение первого поля в структурах всегда нулевое.
TForm1
Но вариант с char raw_memory[sizeof(T) * N]; внутри array мне нравится гораздо больше.
Ну возьму я указатель на последний элемент, приведу его к TStruct (содержимое вылазит за, но мы к нему не обращаемся же!), возвращаем лишь ссылку на t, которое никуда не вылазит.
> Но вариант с char raw_memory[sizeof(T) * N]; внутри array мне нравится гораздо больше.
Тут всё ради того, чтобы уйти от этого.
А если размер - миллион? Идея как раз в том, чтобы охрененно большой массив не содержал переголовы на инициализацию, и при этом не "содержал" неинициализированных полей.
> переголовы на инициализацию
Не будет он инициализироваться. Ча содержит тривиальный конструктор. То есть пустой и он не будет вызываться.
Так тоже можно, но мне показалось, что проще Bytes[N], не надо руками умножать для получения адреса, пусть компилятор умножает.
reinterpret_cast
Вроде же только в сторону char * можно кастовать.
P.S. Хотя, с другой стороны, сторону с char* никто никогда не юзает, поэтому никаких проблем с алиасингом быть не должно.
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
А нельзя вроде бы куда угодно, даже через char.
Если я правильно понял, компилятор не трекает кого там в кого копировали. А просто смотрит по прстоым правилам, приведенным в стандарте:
- один из типов чар - может наехать
- типы одинаковые - может наехать
- типы одного размера - может наехать
...
и т.п.
И исходя из этого задумывается, можно ли ему читать вот этот int *p, или сначала надо положить вон в тот int *q, который был написан пару строчек назад, а не откладывать запись в *q после записи в *p.
Поэтому каст через промежуточный чар будет иметь тот же самый эффект, что и каст напрямую. Ну если я не туплю.
тупо вместо:
буду писать
Или даже так:
А зачем вообще так делать?
b = (int32_t)a или b = static_cast<int32_t>(a) уже не возбуждают?
Ага. Железобетонно. Плюёт на проблемы индейцев. И, кстати говоря, всяко лучше оптимизируется. Т.к. нету лишних прокруток через память, и data flow не порушено. Может быть даже сдвигов не будет, если например int64_t был получен умножением. Или "повезет" (а вернее постараяется компилер), и куски int64_t окажутся в нужных регистрах на 32 битной архитектуре.
> int32_t b = 5;
> std::memcpy(a, &b, sizeof(b));
Кстати говно, от индейцев зависит. При больших индейцах скопирует в старшую часть, а при маленьких - в младшую. Впрочем изначальный код так же "работал".
Да хрен бы знал. Я тоже кучу раз нарушал. Но в gcc не баговало ;)
Часто есть, но он далеко не всегда может предупредить. Т.к. если бы он всегда понимал ситуацию - он бы точно знал, когда можно, а когда нет, и проблемы бы просто не существовало.
В идеале - лучше всего вообще не держать разнотипных указателей на одну и ту же локацию в памяти. Ибо не только на алиасинг, но и на выравнивания и на индейцев нарваться можно. Да оно и не особо нужно.
Если приведешь примеры, где тебе понадобились разнотипные указатели на одну локацию - попробуем разобраться.
> интересуют студии 2008 и много младше
7.1 закрывает на это глаза. Шестая чуть ли не просит: "нарушь стрикт алиасинг, пожалуйста, я прошу тебя..."
Каким образом? :)
Примерно так же, как одиноко идущая ночью нетрезвая девушка, одетая в минимум одежды, неявно, всем своим видом просит любви, нежности, ласки...
Ох она какая проказница... Её нужно наказать.
Компилятор: "я писал в этот char *, а теперь мне нужно прочитать тот int *, но они могут ссылаться на одно и то же место, поэтому мне нельзя передвигать запись в char* на потом, за чтение int *, или вообще выкидывать ее".
Или все-таки я туплю?
The converse is not true. Casting a char* to a pointer of any type other than a char* and dereferencing it is usually in volation of the strict aliasing rule.
Не могу понять, откуда она выводится.
Вот она как раз и падает. И char * походу там вообще не при делах, и всетаки алиасится с чем угодно... Просто в данном примере char в операциях чтения\записи не участвовал, а участвовали uint32_t и uint16_t, о алиасинге которых компилятор не подумал.
Поэтому предположение остается: направление каста ни на что не влияет. Главное - в какие локации памяти я пишу, и из каких читаю.
Можно кастить что угодно куда угодно, и в любом порядке, если я все правильно понял. Сам каст абсолютно ни на что не влияет, т.к. компилятор не трекает их.
Ты мог сами указатели (не то, на что они ссылаются, а сами указатели) через memcpy скопировать вместо каста и получить ту ж самую хуйню.
Ты мог передать их другой функции, и получить ту же самую хуйню.
Ты мог получить в аргументах 2 указателя разного типа, но ссылающихся на одну и ту же хуйню.
Во всех случаях будет такая бага. Хотя каста, как такового, рядом вообще нет.
Не, вот на это там примера вроде как и нет.
В примере к этой фразе писали в int16_t*, а читали из заалиасенного с ним int32_t. char* там вообще сбоку валялся, для красоты.
Это пример как раз на твой случай: "а давайте наебем компилятор, и кастанем не напрямую, а через char *"
Если тебя интересует нарушение строго аналисинга, то UB с его участием в приведенном фрагменте нет.
Правда у меня не char*, а uint8_t*, но вроде бы разница не принципиальна.
А УБ с паддингом?
Ну здесь T лежит в TStruct, а char[sizeof(T)] лежит в Bytes. И если компилятор что-то будет дописывать (а в конец структур, емнип, выравнивание не добавляется, только между полей), то допишет он это к обеим структурам.
Не может, т.к. имеет нетривиальный конструктор.
char [sizeof(T[N])]
Может нельзя? Выравнивание между T может вставить.
empty set of N sub-objects of type T.
Вот тут как раз выравнивания быть не может.
+
tiny_buffer(std::size_t n)
=
tiny_buffer(std::size_t n, const value_type & v = value_type())
for (std::size_t i = 0; i < size_; ++i) data()[i].~value_type();
=
shrink(0) // если нам пофиг на лишнее присваивание в size_, но можно и правда вынести оба тела в какой-нибудь unfill.
No
)))
Инстанциируются и проходят полную проверку всегда только те члены шаблонов класса, которые реально используются. Если ты не вызываешь член, который требует конструктора по умолчанию, то и ошибки компиляции не будет.
В частности, это применимо к перегруженным функциям: если одна из перегрузок вызывает ошибку компиляции, она просто исключается из рассмотрения, см. "Substitution failure is not an error"
std::unfill
Повыше тоже
- спойлеры
- личку
- ссылки
|---------|
|---------|
|---------|
███████
███████
███████
RFC2616
а чё std::destroy нема?
как лишп говно выше намекнул, к std::uninitialized_fill нужен std::initialized_unfill
вынести в array<void*, TypeSize, N> это хорошая идея, ибо меньше пустоо дублирования кода, но я до такого колдунства ещё не дорос.
Я слишком ленив, чтобы писать std::move на двести строк.
Чё?
на самом деле это больше к вопросу о том, что если язык имеет слишком низкой уровень, то для того, чтобы заставить компилятор правильно себя вести во всех случаях, то надо пиздец как изъебнуться
но вопрос оптимизации под конкретные случаи бесконечен, и если им задрачиваться по полной, то в конце концов ещё и окажется, лол, что без шаблонов кода не больше
Кто такие сильки? Массивы/вектора ссылок языком запрещены, так можно работать только с указателями.
Насколько я понимаю, теперь уже вообще не нужен CanRef, т.к. общую реализацию для "силек" и значений у тебя сделать не получилось. Вроде бы легко можно обойтись гораздо более прозрачной специализацией шаблонов.
да, реализации разные
Зачем нарушать семантику языка?
Я ПОШЁЛ ПРОТИВ СИСТЕМЫ
(первый вариант был более двусмысленный)
http://codepaste.ru/16746/
codepaste.ru содержит материалы «для взрослых»
В настройках вашей сети запрещён показ страниц с подобных ресурсов.
Потому что у меня в коде везде инт, и необходимость каждый раз кастить для индексации, чтобы компилятор не ебал мозг, меня угнетает.
И да, я пиздец как много потеряю от того, что я не смогу завести больше 2млрд элементов.
> если это единственное замечание
Сейчас пока времени нет нормально ревьюить, знаковость просто первой в глаза бросилась...
да только вот гемор с кастами лишний при этом получается
> В частности unsigned избавляет от необходимости проверять положительность индексов/размеров.
В релизе проверок нет, в дебаге пох, к тому же компилятор сам умеет оптимизировать код в одну проверку:
assert ( unsigned(index) < m_size );
Готовится к переходу на жабу.