2012-10-10 11 views
14

मुझे सदस्य चयनकर्ता के साथ template से offsetof का उपयोग करने की आवश्यकता है। मैं एक तरह से ले कर आए हैं, यदि आप अजीब वाक्य रचना बहाना होगी:सी ++ संकलन-समय टेम्पलेट के अंदर ऑफसेट करें

template <typename T, 
      typename R, 
      R T::*M 
     > 
constexpr std::size_t offset_of() 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
}; 

उपयोग (सबसे अच्छे रूप में कष्टप्रद) सही नहीं है:

struct S 
{ 
    int x; 
    int y; 
}; 

static_assert(offset_of<S, int, &S::x>() == 0, ""); 
static_assert(offset_of<S, int, &S::y>() == sizeof(int), ""); 

गैर constexpr रूप में आसान है उपयोग करने के लिए:

template <typename T, typename R> 
std::size_t offset_of(R T::*M) 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
}; 

स्पष्ट नुकसान यह है कि यह संकलन समय पर नहीं किया जाता है (लेकिन आसान उपयोग करने के लिए) पर:

int main() 
{ 
    std::cout << offset_of(&S::x) << std::endl; 
    std::cout << offset_of(&S::y) << std::endl; 
} 

जो मैं खोज रहा हूं वह वाक्यविन्यास गैर-constexpr विविधता है, लेकिन अभी भी पूरी तरह से संकलित समय है; हालांकि, मैं इसके लिए वाक्यविन्यास के साथ नहीं आ सकता। मैं offset_of<&S::x>::value (बाकी प्रकार के लक्षणों की तरह) से भी खुश हूं, लेकिन इसके लिए सिंटैक्स जादू नहीं समझ सकता।

+0

मैं यह पता लगाने की कोशिश कर रहा हूं कि मानक कहां कहता है कि यह वही करता है जो आप उम्मीद करते हैं। लेकिन मुझे यह नहीं मिल रहा है। –

+4

मानक ['ऑफसेटफ'] (http://en.cppreference.com/w/cpp/types/offsetof) के साथ क्या गलत है? –

+0

@ निकोलबोलस मुझे लगता है कि यह नहीं है। 'Nullptr' की कमी नहीं होनी चाहिए (और मुझे लगता है कि '->' dereference के रूप में गिना जाता है) यूबी पहले से ही हो सकता है? लेकिन फिर, 'ऑफसेटोफ' मैक्रो का वीसी संस्करण कोई अलग नहीं है। तो व्यवहार में यह शायद अपरिभाषित की तुलना में परिभाषित परिभाषित है। –

उत्तर

13

निम्नलिखित काम करना चाहिए (क्रेडिट विचार के लिए the answer to this question पर जाएं):

#include <cstddef> 

template <typename T, typename M> M get_member_type(M T::*); 
template <typename T, typename M> T get_class_type(M T::*); 

template <typename T, 
      typename R, 
      R T::*M 
     > 
constexpr std::size_t offset_of() 
{ 
    return reinterpret_cast<std::size_t>(&(((T*)0)->*M)); 
} 

#define OFFSET_OF(m) offset_of<decltype(get_class_type(m)), \ 
        decltype(get_member_type(m)), m>() 

struct S 
{ 
    int x; 
    int y; 
}; 

static_assert(OFFSET_OF(&S::x) == 0, ""); 

ध्यान दें कि जीसीसी में, अंतर्निहित विस्तार करने के लिए offsetof मैक्रो फैलता है जो संकलन समय पर इस्तेमाल किया जा सकता (नीचे देखें)। साथ ही, आपका कोड यूबी को आमंत्रित करता है, यह एक शून्य सूचक को संदर्भित करता है, भले ही यह अभ्यास में काम कर सके, फिर भी कोई गारंटी नहीं है।

#define offsetof(TYPE, MEMBER) __builtin_offsetof (TYPE, MEMBER) 

के रूप में ल्यूक डेंटन से कहा, निरंतर भाव हालांकि वर्तमान में जीसीसी कोड (bug report here देखें) स्वीकार करता है सी ++ 11 मानक के अनुसार एक reinterpret_cast को शामिल नहीं कर सकते हैं। इसके अलावा, मुझे defect report 1384 मिला जो नियमों को कम सख्त बनाने के बारे में वार्ता करता है, इसलिए यह भविष्य में बदल सकता है।

+0

तब मुझे अच्छा लग रहा है। – hvd

+4

एक निरंतर अभिव्यक्ति में 'reinterpret_cast' शामिल नहीं हो सकता है (जब तक मूल्यांकन नहीं किया जाता)। –

+1

@LucDanton: जानकारी के लिए धन्यवाद। मुझे एक [दोष रिपोर्ट 1384] भी मिला (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1384) जो इसके बारे में प्रतिबंधों को कम करने के बारे में बात करता है। –