D.R.Y по сравнению с “избегают макросов”

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

Ваш пересмотренный пример:

double stuff(double a, double b)
{
  struct parameters
  {
    double a, b;
  };

  double f(double x, void * params)
  {
    struct parameters p = (struct parameters *) params;
    double a = p->a, b = b->b;
    return some_expression_involving(a,b,x);
  }
  struct parameters par = {a,b};

  return integrate(&f, &par);     // return added!
}

Поскольку вы говорите, что такие функции, как интегратор, нужны

  double (*f)(double x, void * par);

Я не понимаю, почему вы действительно нужны вложенные функции вообще. Я хотел бы написать:

struct parameters
{
    double a, b;
};

static double f(double x, void *params)
{
    struct parameters p = (struct parameters *) params;
    double a = p->a, b = b->b;
    return some_expression_involving(a,b,x);
}

double stuff(double a, double b)
{
    struct parameters par = { a, b };
    return integrate(f, &par);
}

Приведенный выше код должен работать в C89 (если только не возникает проблема с инициализацией 'par') или C99; эта версия кода предназначена только для C99 с использованием составного литерала для параметра (раздел 6.5.2.5 Составные литералы):

double stuff(double a, double b)
{
    return integrate(f, &(struct parameters){a, b});
}

Скорее всего, у вас всего несколько вариантов 'struct тип параметров. Вам нужно предоставить отдельные (достаточно) значимые имена для различных функций - у вас может быть только одна функция с именем 'f' для исходного файла.

Единственное незначительное преимущество версии с вложенными функциями заключается в том, что вы можете быть уверены, что никакая другая функция, кроме stuff, не вызывает f. Но, учитывая пример кода, это не главное преимущество; статическое определение f означает, что никакая функция вне этого файла не может вызвать его, если не передан указатель на функцию.

6
задан StackedCrooked 16 October 2009 в 00:06
поделиться

12 ответов

Я бы не использовал здесь макрос. Подсказка находится в вашем классе «Описание», который имеет дополнительную функцию-член init , которой нет у других. Таким образом, вы не сможете использовать макрос для его определения, но вместо этого вы бы развернули макрос вручную и добавили дополнительную строку.

Для меня это большее нарушение DRY, чем просто запись всего класса определения. Почти не повторять себя, а делать это только для одного случая, часто оказывается труднее поддерживать постоянное повторение. DRY - это поиск хороших абстракций, а не просто сокращение шаблонов.

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

5
ответ дан 8 December 2019 в 02:02
поделиться

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

template <const char *XULName>
class ElementType : public Element
{
public:
    static const char * Type() { return XULName; }

private:
    friend class Element;
    ElementType(
        Element * inParent,
        const AttributesMapping & inAttributesMapping);
};

char windowStr[]="window";
char buttonStr[]="button";
char labelStr[]="label";

typedef ElementType<windowStr> Window;
typedef ElementType<buttonStr> Button;
typedef ElementType<labelStr> Label;

Практическое правило: шаблоны можно использовать практически для всего, что макросы были необходимо для C.

Примечание по реализации: строковые литералы нельзя использовать непосредственно в качестве аргументов шаблона, потому что они имеют внутреннюю связь - вот почему вам понадобится windowStr и т. д. На практике вам нужно поместите объявления windowStr , buttonStr и labelStr в файл H, а определения этих строк в файл CPP.

39
ответ дан 8 December 2019 в 02:02
поделиться

В качестве альтернативы вы можете подумать о создании кода на отдельном этапе сборки вместо использования препроцессора. Мне нравится cog , но вы можете использовать все, что захотите - таким образом вы получаете полный программный контроль над тем, что создается. (Макросы - это мощный инструмент, но их возможности ограничены.)

5
ответ дан 8 December 2019 в 02:02
поделиться

Я думаю, что макросы могут помочь уменьшить повторение (и, следовательно, риск появления ошибок) на таком низком уровне.

Использование макросов останется очень локализованным и должно сделать код в целом более понятным. Конечно, это также может потребовать некоторых усилий по документации.

4
ответ дан 8 December 2019 в 02:02
поделиться

Используйте все, что упрощает код.

DRY и Avoid Macro преследуют одну и ту же цель: сделать ваш код проще.

  • DRY: избегать повторений
  • Avoid Macro: потому что они могут привести к трудным для диагностики ошибкам компилятора или трудным для диагностики ошибкам (поскольку они обходят границы пространства имен и не учитывают / не поддерживают типы C ++).

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

Однако, принимая во внимание проблемы, которые могут возникнуть при использовании макроса, не забудьте назвать его «безопасно». Включите имя проекта / имя файла в начало, например, чтобы уменьшить потенциальное «противоречие» с существующим макросом.

3
ответ дан 8 December 2019 в 02:02
поделиться

ИМХО этот макрос оправдан. Хотя я думаю, что лучше было бы добавить #undef DECLARE_ELEMENT для предотвращения зависания макросов. (Если вы не планируете использовать этот макрос и в других файлах.)

Обратите внимание, однако, что это будет работать, только если эти классы никогда не будут сильно отличаться (или лучше всего).


Существует еще одно решение с использованием шаблонов. Рассмотрим следующий код

namespace impl
{
    struct ButtonTag;
    struct LabelTag;


    template< typename TypeTag >
    struct NameGenerator;

    template<>
    struct NameGenerator< ButtonTag >
    {
        static const char * getName() { return "button"; }
    };

    template<>
    struct NameGenerator< LabelTag >
    {
        static const char * getName() { return "label"; }
    };


    template< typename TypeTag >
    class SimpleElement : public Element
    {
    public:
        static const char * Type()
        { return NameGenerator< TagType >::getName(); }

    private:
        friend class Element;

        SimpleElement(
            Element * inParent,
            const AttributesMapping & inAttributesMapping);

    };
}

typedef impl::SimpleElement< impl::ButtonTag > Button;
typedef impl::SimpleElement< impl::LabelTag > Label;

. Он несколько более подробный, но без макросов.

2
ответ дан 8 December 2019 в 02:02
поделиться

Остерегайтесь использования макросы, которые заменяют определения класса , если вы планируете использовать инструменты автоматической документации кода, такие как doxygen. Вам нужно будет запустить код через препроцессор перед созданием любой документации. Это, пожалуй, не самое важное соображение, но, тем не менее, о чем стоит подумать.

3
ответ дан 8 December 2019 в 02:02
поделиться

примеры кода

enum Types { BUTTON, LABEL,...}

struct TypeList {
    static const char * Type(const int nID)
    {
         switch(nID) {
         case BUTTON: return "button";
         ...
    }
};

template<ID>
class IElem : public Element
{
private:
    static TypeList m_oTypeList;

public:
    static const char * Type() { return m_oTypeList.Type(ID); }
private:
friend class Element;
    IElem(Element * inParent, const AttributesMapping & inAttributesMapping)
    {...}
};

для нестандартных функций и специализированных

class Button : public IElem<BUTTON>
{
...
}
1
ответ дан 8 December 2019 в 02:02
поделиться

Я мог бы пойти немного дальше и использовать как одиночный, так и двойной хэш при использовании макросов. Единый хэш создает строковые константы и идентификаторы двойной конкатенации для создания новых объединенных.

#define DECLARE_ELEMENT(ElementType)                     \
class C ## ElementType : public Element                  \
{                                                        \
public:                                                  \
    static const char * Type() { return # ElementType; } \
                                                         \
private:                                                 \
    friend class Element;                                \
    C ## ElementType(                                    \
        Element * inParent,                              \
        const AttributesMapping & inAttributesMapping);  \
}

DECLARE_ELEMENT(window); // defines Cwindow
DECLARE_ELEMENT(button); // defines Cbutton
DECLARE_ELEMENT(label);  // defines Clabel

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

#include <stdio.h>

#define OUT( _type ) printf("sizeof(%s) = %d\n", #_type, sizeof(_type))

int main() {
  OUT( char );
  OUT( int );
  OUT( short );
  OUT( long );
  OUT( long long );
  OUT( void* );
  return 0;
}
1
ответ дан 8 December 2019 в 02:02
поделиться

Этот код очень похож на программу, которую Том Каргилл анализирует и повторно собирает в главе 1 своей книги « Стиль программирования C ++ », относящейся к 1992 году. По общему признанию, этот код не использовал макросы для копируют почти идентичные классы, но конечный результат выглядит ужасно похожим отсюда.

0
ответ дан 8 December 2019 в 02:02
поделиться

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

0
ответ дан 8 December 2019 в 02:02
поделиться

Я думаю, что в этом случае можно использовать макросы, но только если вы

  • сможете разработать решение, которое не будет слишком сложным, но покрывает (предпочтительно) все необходимые Элемент структуры классов
  • хорошо документируют макрос
  • знают, что некоторые IDE имеют проблемы со структурами классов, сгенерированными макросами и могут жить с последствиями
0
ответ дан 8 December 2019 в 02:02
поделиться
Другие вопросы по тегам:

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