Re[3]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Clinch  
Дата: 07.09.08 12:11
Оценка: 2 (1)
Здравствуйте, Green Chest, Вы писали:

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


GC>
C>>#define VT std::string

C>>int _tmain(int argc, _TCHAR* argv[])
C>>{
C>>    multiarray<VT> ar(3,10);
C>>    multiarray<VT> ar2(3,10);
    
C>>    ar[10][4][1] = "First";    

C>>    ar2 = ar;

C>>    VT s = ar2[10][4][1];
        
C>>    printf("[ value = %s]
C>>",s.c_str());

C>>    return 0;
C>>}

C>>
GC>



GC>Как передать этот объект функции требующей на вход двумерный(трёх, четырёх) массив?


Хм, я либо не понял в чем тут подвох, но почему бы не так
   // Добавлено в класс определение
  typedef typename VAL_TYPE value_type;
   

  typedef multiarray<std::string> StringArray;

  void someFunction( StringArray &ar )
  {
      StringArray::value_type s = ar[2][2];  // value_type - добавлено в классе ( typedef typename VAL_TYPE value_type )
      fprintf(stdout,"At 2,2 = %s",s.c_str());
  }

  ...
   StringArray sa(4,4);
   
   sa[2][2] = "Hello";
   
   someFunction ( sa );
 ...


GC>Я понимаю как сфориморовать из линейного массива n-мерный... Хотя понимаю как из одномерного двумерный — создали массив указателей и в него записали указатели на части нашего большого одномерного.

GC>Но если у нас в виде одномерного представлен трёхмерный и его нужно представить в "обычном" виде...

ЭЭ...ну так в классе у нас и есть одномерный массив, и его можно трансформировать в любой другой, изменяя поле multiarray::elem_ + ещё реинициализация
добавить метод, что-то вроде multiarray::transformTo(int rowa,int cols), нет ?!

Или мы о разных вещах ? ))

GC>И как написать универсальный метод который будет возращать классический массив (т.е. указатели на указатели на указатели...) мерности, равной мерности хранимого в этом объекте массива?


Ого, вот так хочется ?

 StringArray sa(4,4);
 std::string ***pStr = sa; // !?



GC>

GC>Нужен какой-то не стандартный подход...
GC>Предлагаете забросить мою идею?

Не, бросать не надо...мне тоже делать нечего ! ))
Re: Универсальный шаблон массива (n-мерного, n >= 1)
От: Кодт Россия  
Дата: 08.09.08 06:54
Оценка: 2 (1)
Здравствуйте, Green Chest, Вы писали:

GC>Ещё раз (кратко), я хочу:

GC>Создать объект — многомерный массив. Он должен:
GC>1. выделять память, освобождать память, не утруждая этим клиентский код;

Ну это дело нехитрое. std::vector умеет всё, тут даже велосипедировать не нужно.

GC>2. иметь возможность его использования там где требуется int** (на месте инта — любой тип, на месте двух звёздочек — любое количество звёздочек (равное мерности массива));


А вот здесь бы я попробовал рефакторить клиентский код.

Дело в том, что правильный тип гиперкуба — это int *const *const ... *const. То есть, мы вправе менять элементы, но не вправе менять структуру.
К тому же, int*** — это не гиперкуб, а в общем случае jagged array. Каждый подмассив может иметь произвольный размер.

Так что правильный подход состоит в том, чтобы
— абстрагироваться от реализации operator[] (не завязываться на применение его к T* или (T&)[N])
— напротив, не упускать размеры (которые в случае T* приходится брать откуда-то извне)

GC>3. уметь менять свой размер по требованию, уже после создания (т.е. что-то типа reSize, newSize описанных выше);


Вопрос в цене этой операции.
Когда-то на RSDN была статья о том, как создать матрицу с очень быстрой вставкой-удалением и строк, и столбцов. Но естественно, что внутреннее представление там было весьма затейливое.

Думаю, что здесь тебе важно осмыслить: что происходит с данными, когда ты меняешь размеры. Не "как это реализовать" — это второй вопрос. А
— что остаётся на месте, что двигается;
— какие асимптотики;
Например, однократное изменение размера вектора — линейно, так как требует копирования всех данных в новый блок; серия изменений — благодаря резервированию — получается дешевле; а у Б-дерева из-за блочной структуры — и однократное изменение логарифмично.

Ну и, чтоб два раза не вставать:
— нужно ли получать срезы по произвольным размерностям; нужно ли получать подмассивы; какие асимптотики у их порождения;
— какие асимптотики у доступа к отдельным элементам; как эффективно использовать кэш;
Здесь дело в том, что если матрица большого размера развёрнута по строкам, а ты бегаешь по столбцам — они будут вытеснять друг друга. То есть — это вопрос типичного применения. Где-то удобнее по строкам, где-то по столбцам, а где-то по клеточкам M*M.

Все эти вопросы нужно включить самому себе в ТЗ.

GC>4. использовать конструкторы копирования, перегруженные операторы присваивания для массивов однинаковой мерности и типа, но разных размерностей.


А вот это как раз очень просто.

Способ номер 1. Равняйсь-смирно.
Сделал ресайз (привёл приёмник к размеру источника) да и выполнил присваивание.
Но тут опять удобно абстрагироваться от типа источника. Всё, что от него требуется — это иметь функцию, возвращающую размеры, и функцию доступа к элементу.
Тогда ты получишь код, одинаково приспособленный и к голым int***, и к vector<vector<vector<int>>>, и к твоим контейнерам, и к чему угодно.

Способ номер 2. Выкрасил и выбросил.
Пусть контейнер умеет клонировать свои данные.
Тогда конструктор копирования — это присвоение полям свежесозданного объекта этих клонированных данных.
А оператор присваивания — это создание временного объекта-копии источника, обмен полей с объектом-приёмником и убийство объекта-копии (вместе со старыми данными приёмника).
YourContainer::operator=(YourContainer const& src)
{
    if(this == &src) return;
    YourContainer(src).swap(*this);
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Re: Преобразование T* в T*** при известной размерности
От: Alexander G Украина  
Дата: 07.09.08 13:10
Оценка: 1 (1)
Используется Boost.MultiArray, но фактически класс не привязан к boost::multi_array.
Более удобный интерфейс можно получить, если привязать к boost::multi_array, чтобы он сам брал экстенты из него. Или можно к своему велосипеду прикрутить.
Не тестировал особо, просто набросок

#include <boost/multi_array.hpp>
#include <boost/smart_ptr.hpp>

template<typename element, size_t NumDims>
class multi_array_multi_pointer
{
  template<size_t I>
  struct dim_impl;

  template<>
  struct dim_impl<1>
  {
    typedef typename element * pointer_type; 
    pointer_type ptr;

    void init(element ** pointer, size_t const* dim) 
    {
      ptr = *pointer;
      (*pointer) += *dim;
    }

    pointer_type get() const
    { 
      return ptr;
    }
  };

  template<size_t I>
  struct dim_impl
  {
    typedef dim_impl<I - 1> Inner;
    typedef typename Inner::pointer_type inner_pointer, * pointer_type; 
    boost::scoped_array<Inner> data;
    boost::scoped_array<inner_pointer> pointers;

    void init(element ** pointer, size_t const* dim)
    {
      data.reset(new Inner[*dim]);
      pointers.reset(new inner_pointer[*dim]);
      ptr = pointers.get();
      for (size_t i = 0; i != *dim; ++i)
      {
        data[i].init(pointer, dim + 1);
        pointers[i] = data[i].ptr;
      }
    }

    pointer_type get() const
    { 
      return pointers.get();
    }
  };

  typedef dim_impl<NumDims> dims_t;
  dims_t dims;

public:
  typedef typename dims_t::pointer_type pointer_type;

  multi_array_multi_pointer(element * data, size_t const (&extents)[NumDims])
  {
    dims.init(&data, extents);
  }

  pointer_type get()
  {
    return dims.get();
  }
};

int main()
{
  // Create a 3D array that is 3 x 4 x 2
  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::index index;
  array_type a(boost::extents[3][4][2]);

   // Обращение к элементам
  a[0][0][0] = 1;
  a[2][0][1] = 2;
  a[0][3][0] = 3;
  a[2][3][1] = 4;
  double * i = a.origin();
  
  size_t ss[] = {3, 4, 2};
  multi_array_multi_pointer<double, 3> mp(i, ss);
  
  double *** mp_raw = mp.get();
  printf("%f %f %f %f",
    mp_raw[0][0][0],
    mp_raw[2][0][1],
    mp_raw[0][3][0],
    mp_raw[2][3][1]);
  
  return 0;
}




Каким я вижу интерфейс в случае boost::multi_array:
template<class MultiArray>
class multi_array_raw_pointer
{
  public:
    explicit multi_array_raw_pointer(MultiArray const& array); // принимает класс, реализующий boost::multi_array.
    // плюс неявное копирование и присваивание.
    pointer_type get(); // возвращает T***
    /*  operator pointer_type() - этого не надо */
}

template<class MultiArray>
multi_array_raw_pointer make_raw_pointer(MultiArray const& array)
{
  return multi_array_raw_pointer<MultiArray>(array);
}



использование:

void legacy_api(double***);
typedef boost::multi_array<double, 4> A;
A a;

legacy_api(make_raw_pointer(a).get());

или
multi_array_raw_pointer<A> rp(a);
legacy_api(rp.get());
Русский военный корабль идёт ко дну!
Re[3]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Кодт Россия  
Дата: 08.09.08 10:10
Оценка: +1
Здравствуйте, Green Chest, Вы писали:

К>>К тому же, int*** — это не гиперкуб, а в общем случае jagged array. Каждый подмассив может иметь произвольный размер.


GC>Да, какждый подмассив может иметь произвольный размер, это легко реализовать — конструктор с переменным числом параметров. С помощью рекурсии и reinterpret_cast можно однотипно выделять память для каждой мерности (в другой веточке моей темки — в ответе на пост Alexander G я написал код, который это делает, он пока оторван от объекта (сейчас занимаюсь созданием полноценного класса), но делает он именно то что я хотел).

GC>Мне не нужно куб. Достаточно массив, элементами которого являются массивы, элементами которых, являются массивы, элементами которых являются какие-либо объекты. С помощью рекурсивного однотипного выделения памяти каждой мерности, элементы самого верхнего массива (первой мерности) будут далеко друг от друга, но вообще эта реализация не критична к быстродействию. Она должна работать приемлимо в небольших прикладных программах (например, хранить таблицу double-значений размером так 100х1000 и давать не накладный доступ к её элементам). И она делает даже больше, скорость работы мне вполне нравится (под пятимерный массив 10х10х10х10х10 на моей машинке (простенький компутер) выделяет память (вызывает для каждого создаваемого объекта конструктор, который при проводимых мною замерах содержал одно присваивание int'а int'у) менее чем за 30 мс. Трёхмерный 20х20х20 — менее 1 мс). Она универсальна.

Под словом "гиперкуб" я имел в виду прямоугольный гипер-параллелепипед. То есть, у каждой координаты свой диапазон.
И противопоставил этому jagged array — массив независимых друг от друга массивов.
Самая простая реализация JA — это vector<vector<...>> — каждый элемент ресайзишь как хочешь.



Всё-таки тебе нужно не мыслить в терминах С++ (где там шаблон, где там массив), а в терминах технического задания.
Буквально, сесть и написать:

"Я хочу контейнер, который"
— хранит многомерный массив
— размерность пространства есть свойство типа этого контейнера
— диапазоны, напротив, есть свойство каждого объекта этого типа
— имеет фасад в виде n-кратного применения оператора []
— имеет фасад в виде приведения к голому T *const *const ... *const и, на кой-то чёрт, то же самое без константности (сам себе злобный буратино)
— допускает неторопливый ресайз, поскольку операции ресайза будут редки

И более того, "я хочу контейнер, который будет использоваться для"
— представления тензоров
— представления многомерных картинок
— представления таблиц (i1,i2,...,in)->T
и так далее — здесь ты должен определиться.

Иначе твой труд — с разбега в никуда.



Если думать о T*** как о фасаде, а не как о внутреннем устройстве самого контейнера, то несложно родить адаптер — объект, хранящий T*** плюс всё необходимое.
И порождать его из вектора векторов векторов...
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 07.09.08 08:33
Оценка:
Здесь, на rsdn много раз обсуждались многомерные массивы (представлять как одномерные и проч.), но я среди обсуждений не нашёл именно того, что мне нужно.
Необходимо сделать объект — оболочку для массивов, для удобства работы с массивами. (Проблема стара как мир )
Т.е. обычным на С++ выглядит следующее:

{
     //Размерность первая
    const unsigned int s1 = 4;
     //Размерность вторая
    const unsigned int s2 = 8;
     //Временный массив для передачи данных функции
    int **arr = new int*[s1];
    for(unsigned int i=0; i<s1; i++)
        arr[i] = new int[s2];
        
     //Заполняем массив
    for(unsigned int i=0; i<s1; i++)
        for(unsigned int j=0; j<s2; j++)
            d[i][j] = <какое-то значение>;
            
     //Вызываем некий метод некого объекта(это всё не важно, главное что ему
     //требуется указатель на указатель, являющийся массивом целых чисел)
     //прототипа навроде такого: setAD2(int** a, unsgined int size1, unsigned int size2)
    fv.setAD2(arr, s1, s2);
     //Метод забрал часть данных из двухмерного массива в своё внутренее хранилище,
     //о котором мы ничего не знаем и знать не хотим, массив нам больше не нужен
        
     //Освобождаем память
    for(unsigned int i=0; i<s1; i++)
        delete[] arr[i];
    delete[] arr;
}


Утомляет всё это писать, хочется написать выделение и освобождение памяти раз и навсегда ( для всех массивов), чтобы можно было так:

{
     //Объявили массив и выделили память
    Array<int> a(10);

     //Заполняем массив
    for(unsigned int i=0; i<a.size(); i++)
        d[i] = <какое-то значение>;
    
     //Вызвали функцию требующу указатель на инт, являющийся массивом и размер массива
    funcAr(a.getPoint(), a.size());
    
     //Деструктор вызовется автоматически и 
     //беспокоится о не освобождении памяти не нужно
}


Это легко реализовать с помощью вот такого шаблона (привожу очень упрощенный вид):

template <typename type> class A
{
  private:
     type *arr;
  public:
    A() : arr(0) {};
    A(int x) {arr = new type[x];};
   type& opertor [] (int index) {return arr[index];};
};


Однако в таком случае для n-мерного массива (в примере ниже n=2) потребуется опять таки выделять память для каждого измерения:

{
    A<A<int> > ob1(10);   //Получается память будет выделена только под одно измерение, как выделить под другое?
    a[3][8] = 5;  //ошибка, обращаемся к памяти по "левому" адресу
     //Возможно сделать некий метод класса А под названием getMem, выделяющий память и вызывать его так:
    for(unsigned int i=0; i<ob1.getSize(); i++)
        ob1[i].getMem(10);

    a[3][8] = 5;
     //всё отлично, память выделена
    assert(a[3][8] == 5);
}


Решение рабочее, но не красивое — уж лучше вообще без объектов, просто int** и ему выделять память.
Тогда пришла идея сделать шаблон с параметром, вот такой:

template <typename type, unsigned int size> class A
{
  private:
     type *arr;
  public:
    A() : {arr = new type[size];};
   type& opertor [] (int index) {return arr[index];};
};

Тогда код (1)
{
  A<A<int, 10>, 10> ob1; 
  a[3][8] = 5;  //всё отлично, память выделена
}

...работает. Но не более.
Дальше начинаются извращения, например чтобы присваивать (кажется логичным массиву из 10 целочисленных элементов присвоить массив из 9 целочисленных элементов, просто скопировав 9 значений) надо делать приведение типов. Что-то типа такого (привожу упрощенный вариант):

template <size_t len2, typename U> Arr<T, len> & operator= (Arr<U, len2> & arr2)
{
    for (int j = 0; j < len2; ++j)
        arr[j] = arr2[j];
    return *this;
}


Да и вообще в коде (1) мы уже не имеем возможности создавать как таковые динамические массивы, параметру шаблона нужно передавать константу. И функции, требующей на входе двумерный массив, не понятно нак передавать его, если он хранится в виде A<A<int, 10>, 10>, получается одномерный массив объектов (одномерных массивов целых чисел) — это не то что нам нужно.

Итак. Я хочу создать некий пользовательский тип(класс), который позволит работать вот так (примерно так):

{
    Array<Array<int>> a; //Здесь каким-то образом задаю обе размерности
    a[7][5] = 5;
    std::cout << a[7][5] << endl;
    Array<Array<int>> b; //Другого размера, нежели a
    b = a; //Копирует значения (те которые поместятся, т.к. размер может быть и меньше)
    b.reSize(50); //Перевыделяет память и копирует значения
    a.newSize(100); //Освобождает занятую память и выделяет новый блок под сто элементов
    Array<Array<int>> c(b); //Конструктор копирования, если у c и b разные мерности,
     //то хочется чтобы такой код был определён как ошибачный на стадии компиляции
     //И главная цель - вызов функции с прототипом похожим на вот такой func(int** a, int s1, int s2),
     //каким-нибудь простым способом, например таким:
    func(a.getPoint(), a.size(), a[0].size());    
}



Спасибо большое всем кто прочитал и что-то придумал.



Ещё раз (кратко), я хочу:
Создать объект — многомерный массив. Он должен:
1. выделять память, освобождать память, не утруждая этим клиентский код;
2. иметь возможность его использования там где требуется int** (на месте инта — любой тип, на месте двух
звёздочек — любое количество звёздочек (равное мерности массива));
3. уметь менять свой размер по требованию, уже после создания (т.е. что-то типа reSize, newSize описанных
выше);
4. использовать конструкторы копирования, перегруженные операторы присваивания для массивов однинаковой
мерности и типа, но разных размерностей.



Помогите, поделитесь, пожалуйста, любыми мыслями (отсылать меня использовать готовые библиотеки не нужно, посылать совсем далеко то же), советами, как реализовать эти 4 пункта!
Искал, то что хочу не нашёл. В std есть valarry, но 2 пункт (а для него всё это и делалось) не удет с ними работать, да и 4 прийдётся переписать самому. Перечитал главу про шаблоны у Б. Страуструпа, прочитал про шаблоны у Р. Лафоре, листал А. Александреску (жёстко написано, не дорос я ещё до него).
Заранее огромное спасибо всем, кто хоть что-то напишет, прочитав моё сообщение!
(Я уже давно е...сь с этой идеей. (е...сь — еграюсь ))
Re: Универсальный шаблон массива (n-мерного, n >= 1)
От: Alexander G Украина  
Дата: 07.09.08 09:16
Оценка:
Здравствуйте, Green Chest,

Смотрели boost::multi_array ?

GC>2. иметь возможность его использования там где требуется int** (на месте инта — любой тип, на месте двух

GC>звёздочек — любое количество звёздочек (равное мерности массива));

Возможно, этого там нет, т.к. внутри массив хранистя как одномерный. Остальное вроде есть.
Русский военный корабль идёт ко дну!
Re[2]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 07.09.08 09:33
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, Green Chest,

AG>Смотрели boost::multi_array ?

Да (теперь ).
   // Create a 3D array that is 3 x 4 x 2
  typedef boost::multi_array<double, 3> array_type;
  typedef array_type::index index;
  array_type A(boost::extents[3][4][2]);

   // Обращение к элементам
  A[0][0][0] = 1;


Но самое лакомое — передавать массив в функции, ждущие указателя на указатель. Даже если для этого потребуется создать ещё один массив, временный, и скопировать в него всё.
Есть исходный код библиотек Boost, он свободно распространяется(это open-source проект?)?
Мне нужно доделать именно свою реализацию, понять как это работает.
Re[3]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Alexander G Украина  
Дата: 07.09.08 09:59
Оценка:
Здравствуйте, Green Chest, Вы писали:

GC>Но самое лакомое — передавать массив в функции, ждущие указателя на указатель. Даже если для этого потребуется создать ещё один массив, временный, и скопировать в него всё.


Зачем копировать сами элементы ? Нужен просто массив [из массивов [из массивов ...]] указателей. Думаю, можно его реализовать как отдельный класс, не меняя multi_array.
Посмотрите, нет ли вообще готового в multi_array.

GC>Есть исходный код библиотек Boost, он свободно распространяется(это open-source проект?)?


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

multi_array и всё на что она зависит вообще header-only.

Вообще, перед тем как изобретать велосипед, стоит поискать готовое. Особенно поискать в бусте. Например здесь
Автор: jazzer
Дата: 29.08.08
и здесь
Автор: jazzer
Дата: 05.09.08
меня предотвратили от "самодеятельности".
Русский военный корабль идёт ко дну!
Re: Универсальный шаблон массива (n-мерного, n >= 1)
От: Clinch  
Дата: 07.09.08 10:53
Оценка:
Здравствуйте, Green Chest, Вы писали:

GC>Здесь, на rsdn много раз обсуждались многомерные массивы (представлять как одномерные и проч.), но я среди обсуждений не нашёл именно того, что мне нужно.

GC>Необходимо сделать объект — оболочку для массивов, для удобства работы с массивами. (Проблема стара как мир )
GC>Т.е. обычным на С++ выглядит следующее:

GC>
GC>{
GC>     //Размерность первая
GC>    const unsigned int s1 = 4;
GC>     //Размерность вторая
GC>    const unsigned int s2 = 8;
GC>     //Временный массив для передачи данных функции
GC>    int **arr = new int*[s1];
GC>    for(unsigned int i=0; i<s1; i++)
GC>        arr[i] = new int[s2];
        
GC>     //Заполняем массив
GC>    for(unsigned int i=0; i<s1; i++)
GC>        for(unsigned int j=0; j<s2; j++)
GC>            d[i][j] = <какое-то значение>;
            
GC>     //Вызываем некий метод некого объекта(это всё не важно, главное что ему
GC>     //требуется указатель на указатель, являющийся массивом целых чисел)
GC>     //прототипа навроде такого: setAD2(int** a, unsgined int size1, unsigned int size2)
GC>    fv.setAD2(arr, s1, s2);
GC>     //Метод забрал часть данных из двухмерного массива в своё внутренее хранилище,
GC>     //о котором мы ничего не знаем и знать не хотим, массив нам больше не нужен
        
GC>     //Освобождаем память
GC>    for(unsigned int i=0; i<s1; i++)
GC>        delete[] arr[i];
GC>    delete[] arr;
GC>}
GC>


GC>Утомляет всё это писать, хочется написать выделение и освобождение памяти раз и навсегда ( для всех массивов), чтобы можно было так:


GC>
GC>{
GC>     //Объявили массив и выделили память
GC>    Array<int> a(10);

GC>     //Заполняем массив
GC>    for(unsigned int i=0; i<a.size(); i++)
GC>        d[i] = <какое-то значение>;
    
GC>     //Вызвали функцию требующу указатель на инт, являющийся массивом и размер массива
GC>    funcAr(a.getPoint(), a.size());
    
GC>     //Деструктор вызовется автоматически и 
GC>     //беспокоится о не освобождении памяти не нужно
GC>}
GC>


GC>Это легко реализовать с помощью вот такого шаблона (привожу очень упрощенный вид):


GC>
GC>template <typename type> class A
GC>{
GC>  private:
GC>     type *arr;
GC>  public:
GC>    A() : arr(0) {};
GC>    A(int x) {arr = new type[x];};
GC>   type& opertor [] (int index) {return arr[index];};
GC>};
GC>


GC>Однако в таком случае для n-мерного массива (в примере ниже n=2) потребуется опять таки выделять память для каждого измерения:


GC>
GC>{
GC>    A<A<int> > ob1(10);   //Получается память будет выделена только под одно измерение, как выделить под другое?
GC>    a[3][8] = 5;  //ошибка, обращаемся к памяти по "левому" адресу
GC>     //Возможно сделать некий метод класса А под названием getMem, выделяющий память и вызывать его так:
GC>    for(unsigned int i=0; i<ob1.getSize(); i++)
GC>        ob1[i].getMem(10);

GC>    a[3][8] = 5;
GC>     //всё отлично, память выделена
GC>    assert(a[3][8] == 5);
GC>}
GC>


GC>Решение рабочее, но не красивое — уж лучше вообще без объектов, просто int** и ему выделять память.

GC>Тогда пришла идея сделать шаблон с параметром, вот такой:

GC>
GC>template <typename type, unsigned int size> class A
GC>{
GC>  private:
GC>     type *arr;
GC>  public:
GC>    A() : {arr = new type[size];};
GC>   type& opertor [] (int index) {return arr[index];};
GC>};
GC>

GC>Тогда код (1)
GC>
GC>{
GC>  A<A<int, 10>, 10> ob1; 
GC>  a[3][8] = 5;  //всё отлично, память выделена
GC>}
GC>

GC>...работает. Но не более.
GC>Дальше начинаются извращения, например чтобы присваивать (кажется логичным массиву из 10 целочисленных элементов присвоить массив из 9 целочисленных элементов, просто скопировав 9 значений) надо делать приведение типов. Что-то типа такого (привожу упрощенный вариант):

GC>
GC>template <size_t len2, typename U> Arr<T, len> & operator= (Arr<U, len2> & arr2)
GC>{
GC>    for (int j = 0; j < len2; ++j)
GC>        arr[j] = arr2[j];
GC>    return *this;
GC>}
GC>


GC>Да и вообще в коде (1) мы уже не имеем возможности создавать как таковые динамические массивы, параметру шаблона нужно передавать константу. И функции, требующей на входе двумерный массив, не понятно нак передавать его, если он хранится в виде A<A<int, 10>, 10>, получается одномерный массив объектов (одномерных массивов целых чисел) — это не то что нам нужно.


GC>Итак. Я хочу создать некий пользовательский тип(класс), который позволит работать вот так (примерно так):


GC>
GC>{
GC>    Array<Array<int>> a; //Здесь каким-то образом задаю обе размерности
GC>    a[7][5] = 5;
GC>    std::cout << a[7][5] << endl;
GC>    Array<Array<int>> b; //Другого размера, нежели a
GC>    b = a; //Копирует значения (те которые поместятся, т.к. размер может быть и меньше)
GC>    b.reSize(50); //Перевыделяет память и копирует значения
GC>    a.newSize(100); //Освобождает занятую память и выделяет новый блок под сто элементов
GC>    Array<Array<int>> c(b); //Конструктор копирования, если у c и b разные мерности,
GC>     //то хочется чтобы такой код был определён как ошибачный на стадии компиляции
GC>     //И главная цель - вызов функции с прототипом похожим на вот такой func(int** a, int s1, int s2),
GC>     //каким-нибудь простым способом, например таким:
GC>    func(a.getPoint(), a.size(), a[0].size());    
GC>}
GC>



GC>Спасибо большое всем кто прочитал и что-то придумал.


GC>

GC>Ещё раз (кратко), я хочу:
GC>Создать объект — многомерный массив. Он должен:
GC>1. выделять память, освобождать память, не утруждая этим клиентский код;
GC>2. иметь возможность его использования там где требуется int** (на месте инта — любой тип, на месте двух
GC>звёздочек — любое количество звёздочек (равное мерности массива));
GC>3. уметь менять свой размер по требованию, уже после создания (т.е. что-то типа reSize, newSize описанных
GC>выше);
GC>4. использовать конструкторы копирования, перегруженные операторы присваивания для массивов однинаковой
GC>мерности и типа, но разных размерностей.
GC>

GC>

GC>Помогите, поделитесь, пожалуйста, любыми мыслями (отсылать меня использовать готовые библиотеки не нужно, посылать совсем далеко то же), советами, как реализовать эти 4 пункта!
GC>Искал, то что хочу не нашёл. В std есть valarry, но 2 пункт (а для него всё это и делалось) не удет с ними работать, да и 4 прийдётся переписать самому. Перечитал главу про шаблоны у Б. Страуструпа, прочитал про шаблоны у Р. Лафоре, листал А. Александреску (жёстко написано, не дорос я ещё до него).
GC>Заранее огромное спасибо всем, кто хоть что-то напишет, прочитав моё сообщение!
GC>(Я уже давно е...сь с этой идеей. (е...сь — еграюсь ))

Вот придумалось такое ( всякие проверки, методы и прочие ништяки не реализовыавл ).
Подойдёт — докрутишь сам ))


template < typename VAL_TYPE >
class multiarray {
public:
    multiarray ( int dimension,int elements )
        :dim_(dimension)
        ,elem_(elements)
        ,arr ( 0 )
        ,x(0)
    {
        totalSize_ =  ( dim_ + 1 ) *  ( sizeof ( VAL_TYPE ) *  ( elem_ ) );
        arr = new VAL_TYPE [ totalSize_ ];
    }

    ~multiarray()
    {
        delete[] arr;
    }

    multiarray& operator [] (int i)
    {
        ep = i;
        x += i * elem_;        
        return *this;
    }
    
    operator VAL_TYPE& ()
    {
        int p = x - ( elem_ - ep );
        x = 0;
        return arr[p];
    }

    multiarray& operator = ( VAL_TYPE val )
    {
        int p = x - ( elem_ - ep );
        x = 0;

        arr[p] = val;
        return *this;
    }

    multiarray& operator = ( multiarray& rhs )
    {
        x     = ep = 0;
        dim_  = rhs.dim_;
        elem_ = rhs.elem_;
        for ( int i = 0; i< totalSize_;i++ )
            arr[i] = rhs.arr [ i ];
        return *this;
    }

private:
    int ep;
    int x;
    VAL_TYPE *arr;
    int dim_;
    int elem_;
    int totalSize_;
};

#define VT std::string

int _tmain(int argc, _TCHAR* argv[])
{
    multiarray<VT> ar(3,10);
    multiarray<VT> ar2(3,10);
    
    ar[10][4][1] = "First";    

    ar2 = ar;

    VT s = ar2[10][4][1];
        
    printf("[ value = %s]
",s.c_str());

    return 0;
}
Re[2]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 07.09.08 11:28
Оценка:
Здравствуйте, Clinch, Вы писали:

C>#define VT std::string

C>int _tmain(int argc, _TCHAR* argv[])
C>{
C>    multiarray<VT> ar(3,10);
C>    multiarray<VT> ar2(3,10);
    
C>    ar[10][4][1] = "First";    

C>    ar2 = ar;

C>    VT s = ar2[10][4][1];
        
C>    printf("[ value = %s]
C>",s.c_str());

C>    return 0;
C>}

C>



Как передать этот объект функции требующей на вход двумерный(трёх, четырёх) массив?
Я понимаю как сфориморовать из линейного массива n-мерный... Хотя понимаю как из одномерного двумерный — создали массив указателей и в него записали указатели на части нашего большого одномерного.
Но если у нас в виде одномерного представлен трёхмерный и его нужно представить в "обычном" виде...
И как написать универсальный метод который будет возращать классический массив (т.е. указатели на указатели на указатели...) мерности, равной мерности хранимого в этом объекте массива?

Нужен какой-то не стандартный подход...
Предлагаете забросить мою идею?
Re[4]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 07.09.08 11:32
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>здесь.

AG>open-source проект, который можно использовать в коммерческих проектах, причём не обязательно упоминать его в бинарном дистрибутиве.

AG>multi_array и всё на что она зависит вообще header-only.


AG>Вообще, перед тем как изобретать велосипед, стоит поискать готовое. Особенно поискать в бусте. Например здесь
Автор: jazzer
Дата: 29.08.08
и здесь
Автор: jazzer
Дата: 05.09.08
меня предотвратили от "самодеятельности".


Ок, попробую пошариться в его исходниках и понять, что я могу уташить в свой велосипед.
Re[4]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 07.09.08 12:56
Оценка:
Здравствуйте, Clinch, Вы писали:

GC>>Как передать этот объект функции требующей на вход двумерный(трёх, четырёх) массив?


C>Хм, я либо не понял в чем тут подвох, но почему бы не так

C>C> // Добавлено в класс определение
C> typedef typename VAL_TYPE value_type;

C> typedef multiarray<std::string> StringArray;

C> void someFunction( StringArray &ar )
C> {
C> StringArray::value_type s = ar[2][2]; // value_type — добавлено в классе ( typedef typename VAL_TYPE value_type )
C> fprintf(stdout,"At 2,2 = %s",s.c_str());
C> }

C> ...
C> StringArray sa(4,4);

C> sa[2][2] = "Hello";

C> someFunction ( sa );




Функция, которая просит на вход (type** a) описана в недрах закрытой библиотеки. И просить (StringArray &ar) она уже не может. Мы должны принять её такой, какая она есть.

C>Ого, вот так хочется ?

C>C> StringArray sa(4,4);
C> std::string ***pStr = sa; // !?



Да!!! Именно так и хочется, конкретнее:

{
int* ar1;
Array<int> tmp_ar1(10);
ar1 = tmp_ar1.getPoint();

int** ar2;
Array<Array<int>> tmp_ar2(10,20);
ar2 = tmp_ar2.getPoint();

int*** ar3;
Array<Array<int>> tmp_ar3(10,20,5);
ar3 = tmp_ar3.getPoint();

....

int***....** arN; //Здесь указатель на указатели на указатель... в кол-ве N штук
Array<Array<Array<Array<******Array<int>*****>>> tmp_arN(10,20,5, ....,50,10); //Здесь N параметров — размер для каждой мерности
arN = tmp_arN.getPoint();
}





C>Не, бросать не надо...мне тоже делать нечего ! ))

Если это получится, то это будет офигенно удобный динамический массив. Удобней, чем массивы в OP (Delphi).
Re[2]: Преобразование T* в T*** при известной размерности
От: Green Chest Россия  
Дата: 07.09.08 17:19
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>использование:


AG>
AG>void legacy_api(double***);
AG>


Меня это натолкнуло на мысль и я написал для проверки следующее:

class test
{
    private:
        static unsigned int val;
    public:
        int x;
        test() {x = ++val;};
};

unsigned int test::val = 0;

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

int main()
{      
    const unsigned int S = 3;
    int *b = new int[S];
    test ****a = reinterpret_cast<test****>(b);
    int *b_psev;
    int *c;
        
    for(unsigned int i=0; i<S; i++)
    {
        c = new int[S];
        b_psev = reinterpret_cast<int*>(b);
        b_psev[i] = reinterpret_cast<int>(c);
        
        for(unsigned int j=0; j<S; j++)
        {
            c = new int[S];
            b_psev = reinterpret_cast<int*>(b[i]);
            b_psev[j] = reinterpret_cast<int>(c);
            
            for(unsigned int k=0; k<S; k++)
                a[i][j][k] = new test[S];
        }
    }
    
    for(unsigned int i=0; i<S; i++)
    {
        for(unsigned int j=0; j<S; j++)
        {
            for(unsigned int k=0; k<S; k++)
            {
                for(unsigned int l=0; l<S; l++)
                    cout << setw(3) << a[i][j][k][l].x << " ";
                cout << " | " << endl;
            }
            cout << " ||| " << endl;
        }
        cout << endl;
    }
}


Сейчас я это всё ещё доосмысливаю, приведу к нужному виду и наверное будет у меня шаблон многомерного массива
Re[2]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 08.09.08 08:22
Оценка:
Здравствуйте, Кодт, Вы писали:

GC>>1. выделять память, освобождать память, не утруждая этим клиентский код;

К>Ну это дело нехитрое. std::vector умеет всё, тут даже велосипедировать не нужно.

Разумеется, с этим проблем нет никаких.

GC>>2. иметь возможность его использования там где требуется int** (на месте инта — любой тип, на месте двух звёздочек — любое количество звёздочек (равное мерности массива));

К>А вот здесь бы я попробовал рефакторить клиентский код.
К>Дело в том, что правильный тип гиперкуба — это int *const *const ... *const. То есть, мы вправе менять элементы, но не вправе менять структуру.
К>К тому же, int*** — это не гиперкуб, а в общем случае jagged array. Каждый подмассив может иметь произвольный размер.

Да, какждый подмассив может иметь произвольный размер, это легко реализовать — конструктор с переменным числом параметров. С помощью рекурсии и reinterpret_cast можно однотипно выделять память для каждой мерности (в другой веточке моей темки — в ответе на пост Alexander G я написал код, который это делает, он пока оторван от объекта (сейчас занимаюсь созданием полноценного класса), но делает он именно то что я хотел).
Мне не нужно куб. Достаточно массив, элементами которого являются массивы, элементами которых, являются массивы, элементами которых являются какие-либо объекты. С помощью рекурсивного однотипного выделения памяти каждой мерности, элементы самого верхнего массива (первой мерности) будут далеко друг от друга, но вообще эта реализация не критична к быстродействию. Она должна работать приемлимо в небольших прикладных программах (например, хранить таблицу double-значений размером так 100х1000 и давать не накладный доступ к её элементам). И она делает даже больше, скорость работы мне вполне нравится (под пятимерный массив 10х10х10х10х10 на моей машинке (простенький компутер) выделяет память (вызывает для каждого создаваемого объекта конструктор, который при проводимых мною замерах содержал одно присваивание int'а int'у) менее чем за 30 мс. Трёхмерный 20х20х20 — менее 1 мс). Она универсальна.

GC>>3. уметь менять свой размер по требованию, уже после создания (т.е. что-то типа reSize, newSize описанных выше);

К>Вопрос в цене этой операции.
К>Все эти вопросы нужно включить самому себе в ТЗ.

Понимаю. Но третий (и четвёртый) пункт описывает не типичные действия для создаваемого мною объекта, а следовательно под них не стоит его "затачивать". Произвести замеры скорости работы, когда доделаю, интересны циферки-числа?

GC>>4. использовать конструкторы копирования, перегруженные операторы присваивания для массивов однинаковой мерности и типа, но разных размерностей.


К>А вот это как раз очень просто.

Да, как и с пунктом 1 проблем нет, просто в первом моём сообщении в этой теме был вариант с шаблоном, который используется так Array<Array<int,10>,5> — требовались дополнительные "телодвижения" для реализации присваивания. В объекте, который я сейчас делаю проблем с этим нет, т.к. он описывается так: Array<int*****, int, 5> ar(10,15,20,25,30); Здесь параметры шаблоны — это указатель на самую первую мерность (как таковой сам массив), тип элементов (получается что он дублирует первый параметр, только без звёздочек, но как получит из одного другое я не знаю (звездочёк ведь может быть любое кол-во)), мерность массива (опять таки дублируем, в этот раз кол-во звёздочек (если будет пять звёзд, а в мерности будет стоять цифра 4, то обращение ar[0][0][0][0][0] нас отправит явно не в нашу память)). Параметры конструктора — размеры каждой мерности.



Спасибо большое за ответ.
Re[4]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Green Chest Россия  
Дата: 08.09.08 15:42
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Всё-таки тебе нужно не мыслить в терминах С++ (где там шаблон, где там массив), а в терминах технического задания.

К>Буквально, сесть и написать:

К>"Я хочу контейнер, который"

К>- хранит многомерный массив
К>- размерность пространства есть свойство типа этого контейнера
К>- диапазоны, напротив, есть свойство каждого объекта этого типа
К>- имеет фасад в виде n-кратного применения оператора []
К>- имеет фасад в виде приведения к голому T *const *const ... *const и, на кой-то чёрт, то же самое без константности (сам себе злобный буратино)
К>- допускает неторопливый ресайз, поскольку операции ресайза будут редки

Вообще, то что вы написали — мне очень понравилось.
Особенно вот это:
К>- размерность пространства есть свойство типа этого контейнера
К>- диапазоны, напротив, есть свойство каждого объекта этого типа
К>- имеет фасад в виде n-кратного применения оператора []
К>- имеет фасад в виде приведения к голому T *const *const ... *const и, на кой-то чёрт, то же самое без константности (сам себе злобный буратино)
Эти четыре пункта — всё что мне вообще нужно, остальное всё — это уже, так скажем, дополнительные возможности.
Под тензором вы просто понимал матрицу или нечто иное?

К>

К>Если думать о T*** как о фасаде, а не как о внутреннем устройстве самого контейнера, то несложно родить адаптер — объект, хранящий T*** плюс всё необходимое.
Это и без векторов хорошо "рожается".
Re[5]: Универсальный шаблон массива (n-мерного, n >= 1)
От: Кодт Россия  
Дата: 09.09.08 09:28
Оценка:
Здравствуйте, Green Chest, Вы писали:

GC>Вообще, то что вы написали — мне очень понравилось.


Пррроклятье! Я сделал за тебя твою работу!

GC>Под тензором вы просто понимал матрицу или нечто иное?


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

Для тензоров (да и для матриц) существенно итерирование по каждой координате.
Для картинок — быстрый доступ к окресностям точки.
Поэтому там получаются разные стратегии влезания в кэш.
... << RSDN@Home 1.2.0 alpha 4 rev. 1111>>
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.