2009-05-04 6 views
3

Я читал сегодня утром книгу The Pragmatic Programmer Глава 3 основных средств, которые должен иметь каждый программист, и они упомянули Инструменты генерации кода. Они упомянули один скрипт Perl для программ на C++, который помог автоматизировать процесс реализации функций-членов get/set() для частных членов данных.Есть ли скрипт Perl для реализации функций класса/класса для класса C++?

Кто-нибудь знает о таком скрипте и где его найти? Я не смог найти правильные ключевые слова Google, чтобы найти его.

ответ

9

Хотя это не дает прямого ответа на ваш вопрос, вы можете обнаружить, что сгенерированный код фактически не нужен для управления свойствами на C++. Следующий код шаблона позволит вам объявлять и свойство удобно использовать:

// Declare your class containing a few properties 
class my_class { 
public: 
    property<int> x; 
    property<string> y; 
    ... 
}; 

... 

my_class obj; 
cout << obj.x();   // Get 
obj.y("Hello, world!"); // Set 

Вот код:

// Utility template to choose the 2nd type if the 1st is void 
template <typename T, typename U> 
struct replace_void { 
    typedef T type; 
}; 

template <typename T> 
struct replace_void<void, T> { 
    typedef T type; 
}; 

// Getter/setter template 
template <typename T, typename D = void> 
class property { 
    typedef typename replace_void<D, property>::type derived_type; 

    derived_type& derived() { return static_cast<derived_type&>(*this); } 

public: 
    property() {} // May be safer to omit the default ctor 
    explicit property(T const& v) : _v(v) {} 
    property(property const& p) : _v(p._v) {} 
    property& operator=(property const& p) { _v = p._v; return *this; } 

    T operator()() const { return _v; }     // Getter 
    void operator()(T const& v) { derived().check(v); _v = v; } // Setter 

protected: 
    // Default no-op check (derive to override) 
    void check(T const& v) const { (void)v; //avoid unused variable warning} 

private: 
    T _v; 
}; 

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

class nonnegative_int : public property<int, nonnegative_int> { 
public: 
    // Have to redeclare all relevant ctors unfortunately :(
    nonnegative_int(int v) : property<int, nonnegative_int>(v) {} 

    void check(int const& v) const { 
     if (v < 0) { 
      throw "Yikes! A negative integer!"; 
     } 
    } 
}; 

Там у Вас есть это - все преимущества внешне сгенерированных функций геттер/сеттер, ни с кем из беспорядка! :)

Вы можете выбрать check() return a bool с указанием действительности, а не для исключения. И вы могли бы в принципе добавить аналогичный метод, access(), для ловли прочитанных ссылок на свойство.

EDIT: Как г-н Fooz отмечает в комментариях, класс автор может впоследствии изменить реализацию без изменения логической структуры класса (например, путем замены property<int> x элемента с парой x() методов), хотя бинарный совместимость теряется, поэтому пользователям необходимо будет перекомпилировать свой клиентский код всякий раз, когда это делается. Эта способность безболезненно включать будущие изменения на самом деле является основной причиной, по которой люди используют функции getter/setter вместо публичных членов.

Производительность Примечание: Поскольку мы используем CRTP для достижения «время компиляции полиморфизм», нет никакого виртуального вызова накладных расходов для обеспечения вашей собственной check() в подклассе, и вам не нужно объявить его virtual.

+0

Будет ли это работать с частными членами данных? – nmuntz

+1

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

+1

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

3

Поскольку большинство членов C++ не должны быть доступны через функции Get/Set style, это кажется плохой идеей.

Для простого примера того, почему это так, рассмотрим класс std :: string C++. Ее частные члены, вероятно, искать что-то вроде этого (точное выполнение не важно):

private: 
    int last, len; 
    char * data; 

ли вы считаете, что имеет смысл для обеспечения получения/установки элементов для них?

+0

Действительно? Это первый раз, когда я читал, что функции get/set не являются хорошей идеей. Не могли бы вы рассказать о том, почему это плохая идея? Спасибо! – nmuntz

+3

Я не думаю, что вы поняли вопрос. Переменные являются частными, так что класс может осуществлять контроль над тем, как они изменяются, и имеет возможность реагировать на любые изменения, но публичный интерфейс к этим переменным реализуется методами get/set. На языке, таком как C++, где нет способа ретроактивно добавлять элементы управления при мутации члена без изменения общего интерфейса, это совсем не плохая идея. –

+1

Я понимаю, что вы имеете в виду. Не все частные члены данных должны иметь аксессоры и/или мутаторы. Тем не менее, достаточно просто просто удалить методы для нескольких частных членов данных, которые вы не хотите каким-либо образом получать. Точка генератора кода не должна тратить много времени на запись мирского, автоматизированного кода. В идеале язык позволит вам объявлять права доступа для члена. Например, свойства C#. C++ не дает вам этого, поэтому генераторы кода используются для дополнения этого недостатка. –

2

Я не могу помочь вам определить местонахождение этого конкретного сценария. Однако есть инструменты для создания кода quite a lot. Возможно, у вас даже есть то, что вы ищете уже часть своей среды разработки. Для получения дополнительной информации о том, как, почему, и использовать ли инструменты генерации кода, вы можете посмотреть this stackoverflow question. Мне нравится принятый ответ на этот вопрос.

2

Я знаю одного программиста, который uses Perl to augment the C preprocessor когда дело доходит до макросов (latest version of that project). Основная идея заключается вы бы решить на какой-то конвенции сказать сценарий Perl, когда генерировать геттер или сеттера:

struct My_struct { 
    //set 
    //get 
    int x; 

    int y; 

    //get 
    int z; 
}; 

Данный код, как это, я мог бы написать сценарий для поиска строк комментария, состоящих из комментария " get "или" set "в строке (-ях) перед объявлением переменной-члена, а затем заменить их на простые сеттеры/геттеры. В приведенном выше примере я бы сгенерировал void set_x (int i), int get_x() и int get_z() после связанных определений переменных-членов.

Слово предупреждения: не делайте этого на месте с помощью s ///. Вместо этого сканируйте каждую строку по отдельности, и если вы найдете соответствующую строку комментария, то нажмите что-то, что говорит «Мне нужен геттер/сеттер», в стек, а затем, когда вы увидите связанную переменную-член, вытащите вещи из стека и сгенерируйте код ,

В деталях есть несколько чертей, но в целом это идея.

+1

+1. Поскольку синтаксический анализ полностью общих деклараций C++ на самом деле довольно сложный, я делаю ставку на то, что ваш препроцессор Perl распознает только подмножество относительно простых объявлений, что совершенно нормально, но я бы рекомендовал, чтобы ваш сценарий агрессивно жаловался на любое объявление, которое он может 't decode, чтобы вы могли пресечь любые проблемы в зародыше. Всегда лучше быстро терпеть неудачу. –

+0

@j_random_hacker: Хорошая точка. –

2

Вы хотите, чтобы скрипт генерировал функции get/set для всех ваших частных членов без разбора? Это не очень полезный скрипт; вы, вероятно, не найдете его в Google. Если вы хотите каким-то образом пометить свою переменную-член и создать автоматически сгенерированные сгенерированные сгенерированные сгенерированные скелеты и/или сеттеры, то макрос IDE кажется более подходящим. Попробуйте Google для этого.

Смежные вопросы