簡體   English   中英

偽造靜態If在C ++中

[英]Faking Static If in C++

我正在測試各種優化的組合,對於這些我需要靜態 - 如http://channel9.msdn.com/Events/GoingNative/GoingNative-2012/Static-If-I-Had-a-Hammer中所述並禁用特定的優化。 if(const-expr)並不總是有效,因為某些優化涉及更改數據布局,而這在功能范圍內無法完成。

基本上我想要的是這個:

template<bool enable_optimization>
class Algo{
  struct Foo{
    int a;
    if(enable_optimization){
      int b;
    }

    void bar(){
      if(enable_optimization){
        b = 0;
      }
    }
  };
};

(是的,在我的情況下,從數據布局中刪除b的較小內存占用量是相關的。)

目前我正在使用非常糟糕的黑客偽裝它。 我正在尋找一種更好的方法。

檔案啊

#ifndef A_H
#define A_H
template<class enable_optimization>
class Algo;
#include "b.h"
#endif

文件bh(此文件是從Python腳本自動生成的)

#define ENABLE_OPTIMIZATION 0
#include "c.h"
#undef
#define ENABLE_OPTIMIZATION 1
#include "c.h"
#undef

文件ch

template<>
class Algo<ENABLE_OPTIMIZATION>{
  struct Foo{
    int a;
    #if ENABLE_OPTIMIZATION
    int b;
    #endif

    void bar(){
      #if ENABLE_OPTIMIZATION
      b = 0;
      #endif
    }
  };
};

有誰知道更好的方法嗎? 從理論上講,它可以使用模板元編程完成,起初我使用它。 至少我使用它的方式是屁股的痛苦,並導致完全不可讀和臃腫的代碼。 使用上面的hack可以顯着提高生產力。

編輯:我有幾個優化標志,這些互動。

沒有理由使用模板使代碼變得更加復雜:

template<bool enable_optimization>
  class FooOptimized
  {
  protected:
    int b;
    void bar_optim()
    {
      b = 0;
    }
  };

template<>
  class FooOptimized<false>
  {
  protected:
    void bar_optim() { }
  };

template<bool enable_optimization>
  struct Algo
  {
    struct Foo : FooOptimized<enable_optimization>
    {
      int a;

      void bar()
      {
        this->bar_optim();
      }
    };
  };

不需要元編程,只需根據優化是否啟用為新類型並將其專門化來區分不同的部分。

因為新類型在它為空時用作基類(即沒有FooOptimized::b成員),所以它將不占用空間,因此sizeof(Algo<false>::Foo) == sizeof(int)


(可以隨意忽略這個答案的其余部分,它沒有直接解決問題,而是建議采用不同的方法進行不同的權衡。是否“更好”與否取決於真實代碼的細節,在問題中給出的小例子中沒有顯示。)

作為一個相關但又獨立的問題, AlgoAlgo::Foo 依賴於是否啟用優化的部分仍然依賴於模板參數,因此盡管您只編寫一次這些代碼,編譯器將生成兩組目標代碼。 根據代碼中的工作量以及它的使用方式,您可能會發現將其更改為非模板代碼是有利的,即用動態多態替換靜態多態。 例如,您可以將enable_optimization標志設置為運行時構造函數參數而不是模板參數:

struct FooImpl
{
  virtual void bar() { }
};

class FooOptimized : FooImpl
{
  int b;
  void bar()
  {
    b = 0;
  }
};

struct Algo
{
  class Foo
  {
    std::unique_ptr<FooImpl> impl;
  public:
    explicit
    Foo(bool optimize)
    : impl(optimize ? new FooOptimized : new FooImpl)
    { }

    int a;

    void bar()
    {
      impl->bar();
    }
  };
};

您必須對其進行分析和測試,以確定虛擬函數的開銷是否比AlgoAlgo::Foo中不依賴於模板參數的代碼重復更少。

注意:就目前而言,這種方法不起作用,因為在沒有為該成員分配空間的情況下似乎無法在類中擁有成員。 如果您知道如何使其工作,請隨時編輯。

你可以使用這樣的成語:

template<bool optimized, typename T> struct dbg_obj {
  struct type {
    // dummy operations so your code will compile using this type instead of T
    template<typename U> type& operator=(const U&) { return *this; }
    operator T&() { return *static_cast<T*>(0); /* this should never be executed! */ }
  };
};
template<typename T> struct dbg_obj<false, T> {
  typedef T type;
};

template<bool enable_optimization>
class Algo{
  struct Foo{
    int a;
    typename dbg_obj<enable_optimization, int>::type b;

    void bar(){
      if(enable_optimization){
        b = 0;
      }
    }
  };
};

如果禁用了優化,則會為您提供正常的int成員。 如果啟用了優化,則b的類型是沒有成員的結構,不會占用任何空間。

當你的方法bar使用什么看起來像一個運行時if以決定是否要訪問b ,而不是像模板特明確編譯時的機制,你在使用操作b必須從虛擬結構為好。 即使相關部分永遠不會被執行,並且編譯器很可能將它們優化掉,但首先是正確性檢查。 因此, b = 0的行也必須為替換類型進行編譯。 這就是虛擬分配和虛擬投射操作的原因。 雖然兩者都足以滿足您的代碼需求,但我還是將它們包括在內,以防它們在某些其他方面證明是有用的,並且如果您需要它們,還可以了解如何添加更多代碼。

Jonathan Wakely提出的靜態解決方案(不是動態解決方案)是要走的路。

這個想法很簡單:

  1. 將“特定”數據隔離到自己的類中(相應地模板化和專門化)
  2. 對該數據的所有訪問都完全移交給專門的類(接口必須是統一的)

然后,為了不產生空間開銷,您可以通過繼承此特殊類的特化來利用EBO(空基優化)。

注意:一個屬性需要占用至少1個字節的空間,基類不在某些條件下(例如為空)。

在你的情況下:

  • b是具體數據
  • 有一個涉及它的操作(包括設置其值)

所以我們可以輕松地構建一個類:

template <typename T> struct FooDependent;

template <>
struct FooDependent<true> {
    int b;
    void set(int x) { b = x; }
};

template <>
struct FooDependent<false> {
    void set(int) {}
};

然后我們可以使用EBO將它注入Foo

struct Foo: FooDependent<enable_optimization> {
    int a;

    void bar() { this->set(0); }
};

注意:在模板化代碼中使用this來訪問基類的成員,或者好的編譯器將拒絕您的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM