2014-06-12 9 views
7

Zastanawiałem się, czy istnieje elegancki sposób rozwiązania tego problemu. Załóżmy, że istnieje wspólny nagłówek npJak uniknąć długiego czasu kompilacji dla #define we wspólnym nagłówku

// common.h 
#ifndef COMMON_H 
#define COMMON_H 

#define ENABLE_SOMETHING 
//#define ENABLE_SOMETHING_ELSE 
#define ENABLE_WHATEVER 
// many others 

#endif 

Teraz ten plik jest dołączany, powiedzmy 100 inne pliki nagłówkowe i różne #define są używane, aby włączyć lub wyłączyć niektóre fragmenty kodu, które są ograniczone do zaledwie 1-2 plików.

Za każdym razem pojedyncza #define została zmieniona cały projekt wydaje się być przebudowany (pracuję nad Xcode 5.1), co ma sens, ponieważ musi być dosłownie zastąpiony całym kodem, a kompilator nie może wiedzieć a priori gdzie jest używany.

Próbuję znaleźć lepszy sposób zarządzania tym, aby uniknąć długich czasów kompilacji, ponieważ te definicje rzeczywiście są wielokrotnie zmieniane. Dzielenie każdego definiowania w ich odpowiednich plikach/plikach może być rozwiązaniem, ale chciałbym, aby w praktyce wszystko było spakowane razem.

Więc zastanawiałem się, czy nie jest to wzór, który jest zwykle używany, aby rozwiązać ten problem, myślałem o konieczności

// common.h 
class Enables 
{ 
    static const bool feature; 
}; 

// common..cpp 
bool Enables::feature = false; 

Czy to będzie semantycznie równoważne podczas kompilacji zoptymalizowaną binarny? (np. kod wewnątrz fałszywych włączeń zniknie całkowicie).

+0

Może PIMPL, w przypadku gdy wykonanie warunkowo coś robi lub nie. –

Odpowiedz

2

Masz dwa odrębne problemy tutaj:

Dzielenie każde określenie w ich odpowiedni plik/pliki mogą być rozwiązaniem, ale chciałbym w praktyczny sposób, aby mieć wszystko pakowane razem.

To jest twój pierwszy problem. Jeśli nie działam poprawnie, jeśli masz więcej niż jeden obszar funkcjonalny, nie jesteś zainteresowany dodaniem nagłówka dla każdego z nich (ale jednego nagłówka dla wszystkiego).

Zastosuj kroki:

  • zrobić podzielić kodu przez funkcjonalność, do różnych nagłówków; Każdy nagłówek powinien zawierać (co najwyżej) to, co zostało włączone przez pojedyncze #define FEATURESET (i być całkowicie agnostyczne dla istnienia makra FEATURESET).

  • zapewnić każdy nagłówek jest kompilowany tylko raz (dodaj #pragma once na początku każdego pliku funkcja header)

  • dodać plik nagłówka wygoda, który wykonuje #if lub #ifdef oparciu o określone cechy i zawiera pliki fabularne zgodnie z wymaganiami: Kod

    // parsers.h 
    // this shouldn't be here: #pragma once 
    
    #ifdef PARSEQUUX_SAFE 
    #include <QuuxSafe.h> 
    #elif defined PARSEQUUX_FAST 
    #include <QuuxFast.h> 
    #else 
    #include <QuuxSafe.h> 
    #endif 
    
    // eventually configure static/global class factory here 
    // see explanation below for mentions of class factory 
    

Klient:

#include <parsers.h> // use default Quux parser 

#define PARSEQUUX_SAFE 
#include <parsers.h> // use safe (but slower) Quux parser 

Więc zastanawiałem się, czy nie jest to wzór, który jest zwykle używany do rozwiązania tego problemu

To twój drugi problem.

Kanonicznym sposobem na włączenie funkcjonalności według funkcji w C++ jest zdefiniowanie API funkcji w zakresie klas bazowych, fabryk klas i programowania do ogólnego interfejsu.

// common.h 
#pragma once 
#include <Quux.h> // base Quux class 

struct QuuxFactory 
{ 
    enum QuuxType { Simple, Feathered }; 
    static std::unique_ptr<Quux> CreateQuux(int arg); 

    static QuuxType type; 
}; 

// common.cpp: 

#include <common.h> 
#include <SimpleQuux.h> // SimpleQuux: public Quux 
#include <FeatheredQuux.h> // FeatheredQuux: public Quux 

std::unique_ptr<Quux> QuuxFactory::CreateQuux(int arg) 
{ 
    switch(type) { 
    case Simple: 
     return std::unique_ptr<Quux>{new SimpleQuux{arg}}; 
    case Feathered: 
     return std::unique_ptr<Quux>{new FeatheredQuux{arg}}; 
    }; 
    // TODO: handle errors 
} 

kod Klient:

// configure behavior: 
QuuxFactory::type = QuuxFactory::FeatheredQuux; 

// ... 

auto quux = QuuxFactory::CreateQuux(10); // creates a FeatheredQuux in this case 

ta ma następujące zalety:

  • to jest proste i nie używa makr

  • jest wielokrotnego użytku

  • zapewnia odpowiedni poziom abstrakcji

  • go nie używa makra (jak w „w ogóle”)

  • rzeczywistych implementacjach hipotetycznej Quux funkcjonalności zawarte są tylko w jednym pliku (jako szczegółach realizacji kompilowane tylko raz). Możesz dołączyć common.h gdziekolwiek chcesz i nie będzie to w ogóle zawierać SimpleQuux.h i FeatheredQuux.h.

Jako ogólną wytyczną powinieneś napisać swój kod, tak że nie wymaga on żadnych makr do uruchomienia. Jeśli to zrobisz, okaże się, że wszelkie makra, które chcesz dodać, są proste do dodania. Jeśli zamiast tego polegasz na makrach od samego początku, aby zdefiniować swój interfejs API, kod będzie bezużyteczny (lub prawie bezużyteczny).

0

Istnieje sposób podziału definicji, ale nadal można użyć jednego nagłówka konfiguracji centralnej.

main_config.h (nie muszą mieć osłonę zawierać lub #pragma once, ponieważ spowodowałoby to dziwne wyniki jeśli main_config.h obejmuje więcej niż jeden raz w jednej jednostce kompilacji):

#ifdef USES_SOMETHING 
#include "something_config.h" 
#endif 

#ifdef USES_WHATEVER 
#include "whatever_config.h" 
#endif 

something_config.h (nie wolno mają osłony z tego samego powodu, co main_config.h):

#define ENABLE_SOMETHING 

Wszystkie pliki źródłowe i nagłówkowe będzie tylko #includemain_config.h Zanim jednak to muszą zadeklarować, jaka część z nich będą się odnosić do:

some_source.cpp:

#define USES_SOMETHING 
#include "main_config.h" 

some_other_file.h:

#define USES_WHATEVER 
#include "main_config.h" 
Powiązane problemy