Указатели C++ на наследование функций членства

У меня есть решение для вас. Возможно, он не самый элегантный, но он работает

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

Вторым этапом является прохождение этого вложенного объекта и создание таблицы, tr и td соответственно со значениями счетчика из шага 1, используемого в качестве значений атрибута rowspan.

Мне даже не нужен jQuery, только VanillaJS.

console.clear();

var data = [
  {
    LEVEL_1: "LEVEL 1",
    LEVEL_2: "LEVEL 1.1",
    LEVEL_3: "LEVEL 1.1.1"
  },
  {
    LEVEL_1: "LEVEL 1",
    LEVEL_2: "LEVEL 1.2",
    LEVEL_3: "LEVEL 1.2.1"
  },
  {
    LEVEL_1: "LEVEL 1",
    LEVEL_2: "LEVEL 1.2",
    LEVEL_3: "LEVEL 1.2.2"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.1",
    LEVEL_3: "LEVEL 2.1.1"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.1",
    LEVEL_3: "LEVEL 2.1.2"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.2",
    LEVEL_3: "LEVEL 2.2.1"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.2",
    LEVEL_3: "LEVEL 2.2.2"
  }
];

const transformData = (data) => {
  let t = {};

  data.forEach((item, index, source) => {
    let l1 = item.LEVEL_1,
        l2 = item.LEVEL_2,
        l3 = item.LEVEL_3;
    t[l1]                         = t[l1]                         || {text: l1, content: {}, count: 0};
    t[l1].content[l2]             = t[l1].content[l2]             || {text: l2, content: {}, count: 0};
    t[l1].content[l2].content[l3] = t[l1].content[l2].content[l3] || {text: l3, content: {}, count: 0};
    t[l1].count++
    t[l1].content[l2].count++
    t[l1].content[l2].content[l3].count++
  })
  return t;
}

const transformDataTable = (tdata) => {

  const table = document.createElement('table');

  for (l1 in tdata) {
    const td1 = document.createElement('th')
    td1.textContent = tdata[l1].text;
    td1.setAttribute('rowspan', tdata[l1].count)
    let done1 = false;

    for (l2 in tdata[l1].content) {
      const td2 = document.createElement('td')
      td2.textContent = tdata[l1].content[l2].text;
      td2.setAttribute('rowspan', tdata[l1].content[l2].count)
      let done2 = false;
      
      for (l3 in tdata[l1].content[l2].content) {
        const td3 = document.createElement('td')
        td3.textContent = tdata[l1].content[l2].content[l3].text;
        td3.setAttribute('rowspan', tdata[l1].content[l2].content[l3].count)
        const tr = document.createElement('tr')
        !done1 && tr.appendChild(td1) && (done1 = true);
        !done2 && tr.appendChild(td2) && (done2 = true);
        tr.appendChild(td3);
        table.appendChild(tr);
      }
    }
  }
  return table;
}


const tdata = transformData(data);
const table = transformDataTable(tdata);


document.body.appendChild(table)
table {
  border-collapse: collapse;
}

td,
th {
  padding: 20px;
  border: 1px solid black;
  text-align: center;
}

С некоторыми изменениями вы можете отсортировать каждый столбец по своему усмотрению

console.clear();

var data = [
  {
    LEVEL_1: "LEVEL 3",
    LEVEL_2: "LEVEL 3.1",
    LEVEL_3: "LEVEL 3.1.1"
  },
  {
    LEVEL_1: "LEVEL 1",
    LEVEL_2: "LEVEL 1.1",
    LEVEL_3: "LEVEL 1.1.1"
  },
  {
    LEVEL_1: "LEVEL 1",
    LEVEL_2: "LEVEL 1.2",
    LEVEL_3: "LEVEL 1.2.1"
  },
  {
    LEVEL_1: "LEVEL 1",
    LEVEL_2: "LEVEL 1.2",
    LEVEL_3: "LEVEL 1.2.2"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.1",
    LEVEL_3: "LEVEL 2.1.1"
  },
  {
    LEVEL_1: "LEVEL 3",
    LEVEL_2: "LEVEL 3.1",
    LEVEL_3: "LEVEL 3.1.2"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.1",
    LEVEL_3: "LEVEL 2.1.2"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.2",
    LEVEL_3: "LEVEL 2.2.1"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.2",
    LEVEL_3: "LEVEL 2.2.2"
  },
  {
    LEVEL_1: "LEVEL 2",
    LEVEL_2: "LEVEL 2.2",
    LEVEL_3: "LEVEL 2.2.3"
  }
  
];

const transformData = (data) => {
  let t = {};

  data.forEach((item, index, source) => {
    let l1 = item.LEVEL_1,
        l2 = item.LEVEL_2,
        l3 = item.LEVEL_3;
    t[l1]                         = t[l1]                         || {text: l1, content: {}, count: 0};
    t[l1].content[l2]             = t[l1].content[l2]             || {text: l2, content: {}, count: 0};
    t[l1].content[l2].content[l3] = t[l1].content[l2].content[l3] || {text: l3, content: {}, count: 0};
    t[l1].count++
    t[l1].content[l2].count++
    t[l1].content[l2].content[l3].count++
  })
  return t;
}

const transformDataTable = (tdata, sort = (a,b) => b-a, sort1 = sort, sort2 = sort1) => {

  const table = document.createElement('table');

  for (l1 of Object.keys(tdata).sort(sort)) {
    const td1 = document.createElement('th')
    td1.textContent = tdata[l1].text;
    td1.setAttribute('rowspan', tdata[l1].count)
    let done1 = false;

    for (l2 of Object.keys(tdata[l1].content).sort(sort1)) {
      const td2 = document.createElement('td')
      td2.textContent = tdata[l1].content[l2].text;
      td2.setAttribute('rowspan', tdata[l1].content[l2].count)
      let done2 = false;
      
      for (l3 of Object.keys(tdata[l1].content[l2].content).sort(sort2)) {
        const td3 = document.createElement('td')
        td3.textContent = tdata[l1].content[l2].content[l3].text;
        td3.setAttribute('rowspan', tdata[l1].content[l2].content[l3].count)
        const tr = document.createElement('tr')
        !done1 && tr.appendChild(td1) && (done1 = true);
        !done2 && tr.appendChild(td2) && (done2 = true);
        tr.appendChild(td3);
        table.appendChild(tr);
      }
    }
  }
  return table;
}

const asc = (a,b) => b-a
const desc = (a,b) => a-b

const tdata = transformData(data);
const table = transformDataTable(tdata, desc, asc);


document.body.appendChild(table)
table {
  border-collapse: collapse;
}

td,
th {
  padding: 20px;
  border: 1px solid black;
  text-align: center;
}

7
задан Beau Simensen 14 March 2009 в 00:16
поделиться

4 ответа

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

class A {
public:
   void doSomething( boost::function< void ( int ) > callback )
   {
      callback( 5 );
   }
};

Затем любое наследование (или внешний класс) может использовать повышение:: свяжите действительно позвоните:

class B {
public:
   void my_method( int a );
};
void test()
{
   B b;
   A a;
   a.doSomething( boost::bind( &B::my_method, &b, _1 ) );
};

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

7
ответ дан 6 December 2019 в 11:53
поделиться

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

0
ответ дан 6 December 2019 в 11:53
поделиться

C++ обеспечивает лучшие конструкции, чем указатели функции членства для Вашей цели. (Вообще говоря, Вам не придется использовать указатели функции в правильно записанном чистом C++). Две опции первое, что пришло на ум:

1) Создайте чистую виртуальную функцию на названной мелкой монете (). Назовите мелкую монету () от doSomething (). На B сохраните перечислимую членскую переменную для указания на то, что Вы хотите сделать и установить его перед вызовом doSomething (). Мелкая монета переопределения () в B, с его реализацией переключают/заключают членское перечисление в корпус на базовый код, который Вы хотите выполнить.

2) Создайте класс DoInterface с единственной мелкой монетой () чистый виртуальный метод. Создайте производные этого для Вашего Класса B, которые имеют указатель на владение B экземпляр и реализуют мелкую монету () вызывание соответствующей функции на B в каждой из Ваших реализаций DoInterface. doSomething () взял бы экземпляр DoInterface в качестве параметра. Это называют объектным обратным вызовом шаблоном.

2
ответ дан 6 December 2019 в 11:53
поделиться

Проблема состоит в том что функция членства B не функция членства A, даже если B происходит из A. Если у Вас есть a void (A::*)(), можно вызвать его на любого A *, независимо от фактического производного типа объекта, на который указывают.

(Тот же принцип относился бы A & также, конечно.)

Принять B происходит из A, и C происходит из A; это было, были возможны рассмотреть a void (B::*)() (скажите) как a void (A::*)(), можно было бы смочь сделать что-то вроде этого:

A *c=new C;
A *b=new B;
void (A::*f)()=&B::fn;//fn is not defined in A
(c->*f)();

И функция членства B был бы назван на объекте типа C. Результаты были бы непредсказуемы в лучшем случае

На основе примера кода и неиспользования принятия чего-то как повышение, я был бы склонен структурировать обратный вызов как объект:

class Callback {
public:
    virtual ~Callback() {
    }

    virtual Do(int a)=0;
};

Затем функция, которая называет обратный вызов, берет один из этих объектов так или иначе, а не простого указателя функции:

class A {
protected:
    void doSomething(Callback *c) {
        c->Do(1234);
    }
};

У Вас мог затем быть один обратный вызов на полученную функцию, Вы интересуетесь вызовом. За мелкую монету, например:

class B:public A {
public:
    void runDoIt() {
        DoItCallback cb(this);
        this->doSomething(&cb);
    }
protected:
    void doIt(int foo) {
        // whatever
    }
private:
    class DoItCallback:public Callback {
    public:
        DoItCallback(B *b):b_(b) {}

        void Do(int a) {
            b_->doIt(a);
        }
    private:
        B *b_;
    };
};

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

class BCallback:public Callback {
public:
    BCallback(B *obj,void (B::*fn)(int)):obj_(obj),fn_(fn) {}

    void Do(int a) {
        (obj_->*fn_)(a);
    }
private:
    B *obj_;
    void (B::*fn_)(int);
};

Это сделало бы мелкую монету как это:

void B::runDoIt() {
    BCallback cb(this,&B::doIt);
    this->doSomething(&cb);
}

Это могло потенциально быть "улучшено", хотя не все читатели могут видеть его вполне таким образом путем шаблонной обработки его:

template<class T>
class GenericCallback:public Callback {
public:
    GenericCallback(T *obj,void (T::*fn)(int)):obj_(obj),fn_(fn) {}

    void Do(int a) {
        (obj_->*fn_)(a);
    }
private:
    T *obj_;
    void (T::*fn_)(int);
};

Используя это, функция runDoIt выше могла стать:

void B::runDoIt() {
    GenericCallback<B> cb(this,&B::doIt);
    this->doSomething(&cb);
}

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

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

8
ответ дан 6 December 2019 в 11:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: