Есть ли способ лучше? Пока зацикливается и продолжается - PullRequest
2 голосов
/ 11 ноября 2009

В коде, который я поддерживаю, есть много функций, которые можно описать как шаблонные. Вот шаблонная схема, которая повторяется до тошноты по всему приложению при обработке ввода-вывода БД с помощью курсора:

if( !RowValue( row, m_InferredTable->YearColumn(), m_InferredTable->YearName(), m_InferredTable->TableName(), value )
        || !IsValidValue( value ) )
    {
        GetNextRow( cursor, m_InferredTable );

        continue;
    }
    else
    {
        value.ChangeType(VT_INT);
        element.SetYear( value.intVal );
    }

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

Редактировать: Спасибо всем, кто прокомментировал. Вот почему я люблю этот сайт. Вот расширенный вид:

while( row != NULL )
{
    Element element;
    value.ClearToZero();
    if( !GetRowValue( row, m_InferredTable->DayColumn(), m_InferredTable->DayName(), m_InferredTable->TableName(), value )
        || !IsValidValue( value ) )
    {
        GetNextRow( cursor, m_InferredTable );

        continue;
    }
    else
    {
        value.ChangeType(VT_INT);
        element.SetDay( value.intVal );
    }

И так продолжается дальше. Не все значения, взятые из «строки», являются целыми числами. Последнее предложение в цикле while - «GetNextRow».

Ответы [ 6 ]

4 голосов
/ 11 ноября 2009

Хорошо, из того, что вы сказали, у вас есть что-то вроде структуры:

while (row!=NULL)  {
    if (!x) {
        GetNextRow();
        continue;
   }
   else {
       SetType(someType);
       SetValue(someValue);
   }
   if (!y) {
       GetNextRow();
       continue;
   }
   else {
       SetType(SomeOtherType);
       SetValue(someOtherValue);
   }
// ...

   GetNextRow();   
}

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

while (row != NULL) {
    if (x) {
        SetType(someType);
        SetValue(someValue);
    }
    else if (y) {
        SetType(someOtherType);
        SetValue(SomeOtherValue);
    }
    // ...
    GetNextRow();
}

Редактировать: Другая возможность - написать код в виде цикла:

for (;row!=NULL;GetNextRow()) {
    if (!x) 
        continue;
    SetTypeAndValue();
    if (!y)
        continue;
    SetTypeandValue();
    // ...

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

// We never use the base template -- it just throws to indicate a problem.
template <class T>
SetValue(T const &value) { 
   throw(something);
}

// Then we provide a template specialization for each type we really use:
template <>
SetValue<int>(int value) {
    SetType(VT_INT);
    SetValue(value);
}

template <>
SetValue<float>(float value) { 
    SetType(VT_FLOAT);
    SetValue(value);
}

Это позволяет объединить пару вызовов для установки типа и значения в один вызов.

Редактировать: Что касается обработки сокращения, это зависит от того, является ли анализ столбца дорогостоящим (достаточно, чтобы о нем заботиться), вы можете просто вкладывать свои условия:

if (x) { 
    SetTypeAndValue();
    if (y) {
        SetTypeAndValue();
        if (z) { 
            SetTypeAndValue();

и так далее. Основным недостатком этого является то, что он будет довольно глубоко вложенным, если (как вы сказали) у вас более 20 условий в одном цикле. В таком случае, я бы, наверное, задумался над версией для цикла for, которую я дал выше.

2 голосов
/ 11 ноября 2009

Почему бы не сделать функцию, выполняющую всю работу?

bool processElement(Element& element, Row* row, int value, Table& m_InferredTable, /*other params*/)
{
    if( !GetRowValue( row, m_InferredTable->DayColumn(), m_InferredTable->DayName(), m_InferredTable->TableName(), value )
            || !IsValidValue( value ) )
    {
            GetNextRow( cursor, m_InferredTable );
            return true;
    }
    else
    {
            value.ChangeType(VT_INT);
            element.SetDay( value.intVal );
    }
    return false;
}

В вашем цикле

while (row != NULL)
{
    if (processElement(element, row, value, m_InferredTable))
        continue;
    // other code
}
0 голосов
/ 11 ноября 2009

Вы можете удалить все вызовы GetNextRow и предложения else:

for (row = GetFirstRow () ; row != null ; GetNextRow ())
{
    Element element;
    value.ClearToZero();
    if( !GetRowValue( row, m_InferredTable->DayColumn(), m_MetInferredOutTable->DayName(), m_MetInferredOutTable->TableName(), value )
            || !IsValidValue( value ) )
    {
      continue;
    }

    value.ChangeType(VT_INT);
    element.SetDay( value.intVal );
}
0 голосов
/ 11 ноября 2009

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

template <typename T> struct ElemTrait<T> {};
template <> struct ElemTrait<int> {
    static inline void set(Val &value, Elem &element) {
        value.ChangeType(VT_INT);
        element.SetYear(value.intVal);
    }
};
// template <> struct ElemTrait<float> { ... };

template <typename T>
void do_stuff( ... ) {
    // ...

    if (!RowValue(row,
        m_InferredTable->YearColumn(),
        m_InferredTable->YearName(),
        m_InferredTable->TableName(), value)
        || !IsValidValue(value)
    ) {
        GetNextRow(cursor, m_InferredTable);
        continue;
    } else {
        ElemTrait<T>::set(value, element);
    }

    // ...
}
0 голосов
/ 11 ноября 2009

Мой инстинктивный подход состоит в том, чтобы создать полиморфный подход здесь, где вы в конечном итоге будете делать что-то вроде (по модулю вашего языка и точной логики):

db_cursor cursor;

while(cursor.valid())
{
  if(cursor.data.valid())
  { 
     process();
  }

  cursor.next();
}

db_cursor будет базовым классом, от которого наследуются ваши классы табличных типов, а дочерние классы будут реализовывать функции валидности.

0 голосов
/ 11 ноября 2009

Почему бы не инвертировать ваш if-тест?

   if (RowValue(row, m_InferredTable->YearColumn(), m_InferredTable->YearName(),  m_InferredTable->TableName(), value ) 
     && IsValidValue( value ))
   {
     value.ChangeType(VT_INT);
     element.SetYear( value.intVal );
   }
   else
   {
     GetNextRow( cursor, m_InferredTable );
   }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...