2012-12-11 21 views
15

क्या यह इस तरह के अस्तित्व के लिए संभव है?क्या सी ++ में लूप के लिए स्थैतिक विकसित करना संभव है?

template<int Channel> 
void deduce_mask(Matrix const &src, int mask[]) 
{ 
    //I hope i could become a constant and the compiler would unroll the loop at compile time   
    for(int i = Channel; i != -1; --i) 
    {    
     //mapper is a helper class which translate two and three dimension into one dimension index 
     //constexpr makes it possible to find out the index at compile time 
     mask[mapper(0, 1, i)] = src(row - 1, col)[i]; 
     mask[mapper(1, 1, i)] = src(row, col)[i]; 
     mask[mapper(2, 1, i)] = src(row + 1, col)[i];  
    } 
} 

template<int Channel> 
class deduceMask 
{ 
public: 
    static void deduce_mask(matrix const &src, int mask[]); 
}; 

template<int Channel> 
void deduce_mask(matrix const &src, int mask[]) 
{     
    mask[mapper(0, 1, Channel)] = src(row - 1, col)[Channel]; 
    mask[mapper(1, 1, Channel)] = src(row, col)[Channel]; 
    mask[mapper(2, 1, Channel)] = src(row + 1, col)[Channel];  

    deduceMask<Channel - 1>::deduce_mask(src, mask); 
} 

template<> 
class deduceMask<-1> 
{ 
public: 
    static void deduce_mask(matrix const &src, int mask[]) 
    { 

    } 
}; 

दूसरा समाधान के बजाय

एकमात्र समाधान मैं की है जब मैं चाहता हूँ आ सकता है संकलक बाहर संकलन time.Do में परिणाम निकालने की मैं लिए एक आसान तरीका है मेट्रोग्रामिंग समाधान जैसे निरंतर मान बनें? मेरे लिए, मेटाप्रोग्रामिंग संस्करण के बजाय काम करने के लिए एक आसान लूप अधिक आसान है।

मेरी खराब अंग्रेजी के लिए खेद है, मुझे उम्मीद है कि मैं अपनी समस्या को ठीक से समझाऊंगा।

+2

यदि आप उस प्रकार के वाक्यविन्यास को पसंद करते हैं तो आप इसे बार-बार लिख सकते हैं और कंस्ट्रैक्स का उपयोग कर सकते हैं? – Agentlien

+0

मैंने एक कॉन्स्टेक्स संस्करण बनाने की कोशिश की लेकिन असफल रहा, constexpr केवल एक रिटर्न स्टेटमेंट की अनुमति देता है। – StereoMatching

+3

मैं काफी हद तक निश्चित हूं कि अधिकांश आधुनिक कंपाइलर स्वचालित रूप से इस अनुकूलन को करते हैं, जैसे कि वे निरंतर मान तक 'फॉर' लूप के लिए करते हैं (उदा। '(Int i = 0; i <5; i ++) ')। हालांकि आपको सुनिश्चित करने के लिए जांच करनी होगी। – ShdNx

उत्तर

20

सी ++ में टेम्पलेट मेटाप्रोग्रामिंग शुद्ध कार्यात्मक प्रोग्रामिंग है, और शुद्ध कार्यात्मक प्रोग्रामिंग में आपको लूप का उपयोग करने की आवश्यकता नहीं है या आप के दौरान और आपके पास कोई भी परिवर्तनीय डेटा नहीं है। आपके पास सब कुछ रिकर्सन है। रिकर्सन के साथ काम करने में आसान बनाने के लिए, आपको थोड़ा सा अवशोषण स्तर बढ़ाना होगा। पुनरावर्ती कोड है कि आप ठीक है, लेकिन यात्रा और काम के अलावा विभाजित किया जा सकता:

template <int First, int Last> 
struct static_for 
{ 
    template <typename Fn> 
    void operator()(Fn const& fn) const 
    { 
     if (First < Last) 
     { 
      fn(First); 
      static_for<First+1, Last>()(fn); 
     } 
    } 
}; 

template <int N> 
struct static_for<N, N> 
{ 
    template <typename Fn> 
    void operator()(Fn const& fn) const 
    { } 
}; 

अब आप इस मेटा-समारोह है, तो आप इस तरह अपने deduce_mask समारोह लिख सकते हैं:

template<int Channel> 
void deduce_mask(Matrix const &src, int mask[]) 
{ 
    static_for<0, Channel>()([&](int i) 
    {    
     mask[mapper(0, 1, i)] = src(row - 1, col)[i]; 
     mask[mapper(1, 1, i)] = src(row, col)[i]; 
     mask[mapper(2, 1, i)] = src(row + 1, col)[i];  
    }); 
} 

विजुअल C++ 2012/OB1 कमांड लाइन स्विच के साथ इस इस कोड को संकलित करता है:

push  0 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  1 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  2 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  3 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
push  4 
call  <lambda_7588286c1d4f3efe98a2e307bd757f8e>::operator() (010C1270h) 
... 

आप लैम्ब्डा कार्यों का उपयोग नहीं कर सकते हैं, तो आप एक functor लिखने के लिए की जरूरत है। लैम्बडा फ़ंक्शन पर फ़ैक्टर का एक फायदा है - आप एक कॉलिंग कन्वेंशन निर्दिष्ट कर सकते हैं (यदि आपको ऐसा करने में कोई फर्क नहीं पड़ता)। यदि मज़ेदार के ऑपरेटर() में __fastcall कॉलिंग सम्मेलन है तो आप असेंबलर कोड में push x के बजाय mov edx, x देखेंगे।

+0

धन्यवाद, यह उत्तर सुंदर सुरुचिपूर्ण है (कम से कम मेरे लिए) – StereoMatching

+5

लेकिन लूप के लिए सामान्य होने की तुलना में उन सभी कॉल की धीमी गति से नहीं हैं? संकलक उन्हें अनुकूलित क्यों नहीं करता है? – Kapichu

2

लेगो की प्रतिक्रिया है, जबकि सुंदर और भयानक, अगर आप सूचकांक किसी टेम्प्लेट में जाना चाहता हूँ संकलन नहीं करेगी - उदा std::get<i>(some_tuple)

मामले में आप भविष्य में इस अतिरिक्त सुविधा को लागू करना चाहते हैं, नीचे दिए गए कोड काम करेंगे और लेगो समाधान के साथ पीछे की ओर-संगत होना चाहिए (कि मैं एक स्थिर का उपयोग को छोड़कर ऑपरेटर के बजाय विधि लागू()):

static_for<0, Channel>::apply([&](auto i) // Changed from '(int i)'. In general, 'auto' will be a better choice for metaprogramming! 
{    
    // code... 
    mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change 
    std::get<i.value>(some_tuple); // But here you must get the member .value 
    // more code... 
}); 

कुलपति में परीक्षण किया गया ++ 2015 मैं अनुसंधान नहीं किया क्यों यह काम करता है, लेकिन मैं केवल va का उपयोग कर यह मान सकते हैं T है कि std::integral_constant<T,...> एक अंतर्निहित डाली परिभाषित करता है:

template <int First, int Last> 
struct static_for 
{ 
    template <typename Lambda> 
    static inline constexpr void apply(Lambda const& f) 
    { 
     if (First < Last) 
     { 
      f(std::integral_constant<int, First>{}); 
      static_for<First + 1, Last>::apply(f); 
     } 
    } 
}; 
template <int N> 
struct static_for<N, N> 
{ 
    template <typename Lambda> 
    static inline constexpr void apply(Lambda const& f) {} 
}; 

अब आप निम्न कर सकते हैं लुई, लेकिन संकलक यह नहीं समझ सकता कि अंतर्निहित कास्ट constexpr उत्पन्न करता है, इसलिए आपको i.value का उपयोग करके मूल्य पुनर्प्राप्त करना होगा, जो constexpr है।

टिप्पणी में @ टॉम के सवाल को संबोधित करते आप एक पैरामीटर पैक, तो आपको निम्न (समान कार्यान्वयन) कर सकते हैं से अधिक पुनरावृति करना चाहते हैं: std::get<N.value>(std::make_tuple(args...)) बदसूरत लग रहा है

template<typename... Args> 
inline constexpr auto foo(const Args&... args) 
{ 
    static_for<0,sizeof...(Args)>::apply([&](auto N) 
    { 
     std::cout << std::get<N.value>(std::make_tuple(args...)); 
    }); 
} 

foo(1,"a",2.5); // This does exactly what you think it would do 

हैं, तो आप एक और बना सकते हैं constexpr फ़ंक्शन जो कोड को कम करता है।

+0

मुझे लगता है कि आपको '(int i)' to '(auto i) 'को बदलने की आवश्यकता है, क्योंकि' int :: value' बीमार है – Caleth

+0

@ कैलेथ अच्छा पकड़! एकाधिक स्रोतों से कॉपी/चिपकाने का नतीजा। – AOK

+0

कमाल! यह मेरे कोड को और अधिक पठनीय बनाता है। – tom

2

if constexpr के साथ हम एओके के समाधान पर सुधार कर सकते हैं।

template <int First, int Last, typename Lambda> 
inline void static_for(Lambda const& f) 
{ 
    if constexpr (First < Last) 
     { 
     f(std::integral_constant<int, First>{}); 
     static_for<First + 1, Last>(f); 
     } 
} 
इस के साथ

हम चाहते हैं कि ::apply

static_for<0, Channel>([&](auto i) 
{    
    // code... 
    mask[mapper(0, 1, i)] = src(row - 1, col)[i]; // Notice that this does not change 
    std::get<i.value>(some_tuple); // But here you must get the member .value 
    // more code... 
}); 

से छुटकारा पा सकते दुर्भाग्य से आप अभी भी i.value लिखने के लिए की है।


ध्यान दें कि यह if constexpr बिना संभव नहीं होगा क्योंकि AOK के रास्ते static_for की आंशिक टेम्पलेट विशेषज्ञता की आवश्यकता होगी।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^