В C ++, как я могу избежать #include заголовочного файла, когда мне нужно использовать перечисление? - PullRequest
5 голосов
/ 25 марта 2009

В моих заголовочных файлах C ++ я стараюсь использовать предварительные объявления (класс MyClass;) вместо #include заголовка класса, как рекомендуется во многих стандартах кодирования C ++ (один из руководств по стилю Google C ++).

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

//// myclass1.hpp ////

class MyClass1
{
    enum MyEnum1
    {
        Enum_A, Enum_B, Enum_C
    };
};

//// myclass2.hpp ////

// I want to avoid this
#include "myclass1.hpp"

// I'd prefer to do this (forward declaration)
class MyClass1;

class MyClass2
{
    // This is o.k.: I only need to forward declare MyClass1
    MyClass1* ptr;

    // This forces me to #include, but I don't want to!
    void func( MyClass1::MyEnum1 e );
};

Лучшее решение, которое я могу придумать, - заменить перечисления константами-членами:

//// myclass1.hpp  ////

MyClass1
{
    static const int Enum_A;
    static const int Enum_B;
    static const int Enum_C;
};

//// myclass1.cpp ////

const int Enum_A = 1;
const int Enum_B = 2;
const int Enum_C = 3;

Однако в этом случае решение кажется хуже, чем проблема.

В настоящее время я просматриваю Крупномасштабный программный дизайн C ++ (Lakos) и Эффективная работа с устаревшим кодом (Feathers) для методов снятия зависимости, но я не нашел Хорошее решение пока.

Ответы [ 8 ]

12 голосов
/ 25 марта 2009

Это сложно сделать красиво. Возможно, разумным решением было бы переместить enum s в общий заголовочный файл?

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

6 голосов
/ 25 марта 2009

Вы не можете пересылать объявляемые значения enum - и ваш обходной путь - это шаг по пути к полному безумию.

Испытываете ли вы какие-либо серьезные замедления компиляции, вызванные #include заголовками? Если нет, просто включите их. Использование предварительных деклараций не «наилучшая практика» - это хак.

4 голосов
/ 25 марта 2009

C ++ 0x строго типизированные перечисления могут быть объявлены вперед. GCC 4.4.0 и CodeGear C ++ Builder 2009 поддерживают строго типизированные перечисления.

Существует несколько классов, похожих на enum, таких как (предлагается, но никогда не завершается и не принимается) Boost.Enum, доступных для загрузки из Boost Vault по по этой ссылке . Поскольку Boost.Enums являются классами, они могут быть объявлены вперед.

Однако простое перечисление перечислений в отдельный файл (как в этот ответ ) кажется самым простым и лучшим решением (за исключением поддержки C ++ 0x).

4 голосов
/ 25 марта 2009

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

Поскольку переменная enum не является указателем, вы не можете использовать предварительные объявления. И я не думаю, что есть альтернативное решение.

2 голосов
/ 25 марта 2009

Вы можете использовать аргументы шаблона для программирования против «общих» типов перечислений. Примерно так:

// enum.h
struct MyClass1 { enum e { cE1, cE2, cELast }; };

// algo.h
// precondition: tEnum contains enumerate type e
template< typename tEnum > typename tEnum::e get_second() { 
    return static_cast<typename tEnum::e>(1); 
}

// myclass1.h

// myclass.h
template< typename tClass1 >
class MyClass2
{
    tClass1 * ptr;
    void func( tClass1::e e );
};
// main.cpp
#include "enum.h"
#include "algo.h"
int main(){ return get_second<Enum>(); }
1 голос
/ 25 марта 2009

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

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

0 голосов
/ 25 марта 2009

Если вы действительно столкнулись с замедлением компиляции из-за включения заголовка, другой вариант - использовать int вместо enum. Это довольно непопулярный подход, поскольку он снижает безопасность типов. Если вы выберете такой подход, я бы также рекомендовал добавить код для программной проверки границ:

// in class1.h
class Class1 {
public:
    enum Blah {
       kFirstBlah, // this is always first
       eOne = kFirstBlah,
       ...
       kLastBlah // this is always last
    };
};

// in checks.h
#include <stdexcept>
namespace check {
template <typename T, typename U>
U bounds(U lower, T value, U upper) {
    U castValue = static_cast<U>(value);
    if (castValue < lower || castValue >= upper) {
        throw std::domain_error("check::bounds");
    }
    return castValue;
}
} // end check namespace

// in class2.h
class Class2 {
public:
    void func(int blah);
};

// in class2.cpp
#include "class2.h"
#include "class1.h"
#include "checks.h"

void Class2::func(int blah) {
    Class1::Blah blah_;
    blah_ = check::bounds(Class1::kFirstBlah, blah, Class1::kLastBlah);
}

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

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

0 голосов
/ 25 марта 2009

Предварительная декларация перечислений фактически была предложена комитетом по стандартам C ++. См. этот документ (pdf). Это, безусловно, будет хорошей функцией!

...