"Kendin yapmak zorunda olmanın" bariz çözümünü tekrar edeceğim. Bu, kodun hem basit sınıflarla hem de sınıf şablonlarıyla çalışan kısa ve öz C ++ 11 sürümüdür:
#define DECLARE_SELF(Type) \
typedef Type TySelf; /**< @brief type of this class */ \
/** checks the consistency of TySelf type (calling it has no effect) */ \
void self_check() \
{ \
static_assert(std::is_same<decltype(*((TySelf*)(0))), \
decltype(*this)>::value, "TySelf is not what it should be"); \
} \
enum { static_self_check_token = __LINE__ }; \
static_assert(int(static_self_check_token) == \
int(TySelf::static_self_check_token), \
"TySelf is not what it should be")
Bunu ideone'da eylem halinde görebilirsiniz . Bu sonuca götüren oluşum aşağıdadır:
#define DECLARE_SELF(Type) typedef Type _TySelf; /**< @brief type of this class */
struct XYZ {
DECLARE_SELF(XYZ)
};
Bu, kodu farklı bir sınıfa kopyalayıp yapıştırmak ve XYZ'yi değiştirmeyi unutmak gibi bariz bir soruna sahiptir, örneğin:
struct ABC {
DECLARE_SELF(XYZ) // !!
};
İlk yaklaşımım çok orijinal değildi - şöyle bir işlev yapmak:
/**
* @brief namespace for checking the _TySelf type consistency
*/
namespace __self {
/**
* @brief compile-time assertion (_TySelf must be declared the same as the type of class)
*
* @tparam _TySelf is reported self type
* @tparam _TyDecltypeThis is type of <tt>*this</tt>
*/
template <class _TySelf, class _TyDecltypeThis>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;
/**
* @brief compile-time assertion (specialization for assertion passing)
* @tparam _TySelf is reported self type (same as type of <tt>*this</tt>)
*/
template <class _TySelf>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TySelf, _TySelf> {};
/**
* @brief static assertion helper type
* @tparam n_size is size of object being used as assertion message
* (if it's a incomplete type, compiler will display object name in error output)
*/
template <const size_t n_size>
class CStaticAssert {};
/**
* @brief helper function for self-check, this is used to derive type of this
* in absence of <tt>decltype()</tt> in older versions of C++
*
* @tparam _TyA is reported self type
* @tparam _TyB is type of <tt>*this</tt>
*/
template <class _TyA, class _TyB>
inline void __self_check_helper(_TyB *UNUSED(p_this))
{
typedef CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TyA, _TyB>)> _TyAssert;
// make sure that the type reported as self and type of *this is the same
}
/**
* @def __SELF_CHECK
* @brief declares the body of __self_check() function
*/
#define __SELF_CHECK \
/** checks the consistency of _TySelf type (calling it has no effect) */ \
inline void __self_check() \
{ \
__self::__self_check_helper<_TySelf>(this); \
}
/**
* @def DECLARE_SELF
* @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
* @param[in] Type is type of the enclosing class
*/
#define DECLARE_SELF(Type) \
typedef Type _TySelf; /**< @brief type of this class */ \
__SELF_CHECK
} // ~self
Biraz uzun, ama lütfen burada benimle kalın. Bu, C ++ 03 olmadan çalışma avantajına sahiptir decltype
, çünkü __self_check_helper
işlevin türünü çıkarmak için kullanılır this
. Ayrıca, yok static_assert
, ama sizeof()
onun yerine hile kullanılıyor. C ++ 0x için çok daha kısaltabilirsiniz. Şimdi bu şablonlar için çalışmayacak. Ayrıca, makronun sonunda noktalı virgül beklememesi ile ilgili küçük bir sorun var, eğer bilgiçlikle derleme yapılıyorsa, fazladan gereksiz bir noktalı virgülden şikayet edecek (veya XYZ
ve ABC
).
Bir kontrol yapma Type
geçirilir o DECLARE_SELF
sadece kontrol gibi, bir seçenek değildir XYZ
kayıtsız sınıfını (ok olan), ABC
(hata var olan). Ve sonra bana çarptı. Şablonlarla çalışan, ek depolama gerektirmeyen sıfır maliyetli bir çözüm:
namespace __self {
/**
* @brief compile-time assertion (_TySelf must be declared the same as the type of class)
* @tparam b_check is the asserted value
*/
template <bool b_check>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2;
/**
* @brief compile-time assertion (specialization for assertion passing)
*/
template <>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<true> {};
/**
* @def DECLARE_SELF
* @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
* @param[in] Type is type of the enclosing class
*/
#define DECLARE_SELF(Type) \
typedef Type _TySelf; /**< @brief type of this class */ \
__SELF_CHECK \
enum { __static_self_check_token = __LINE__ }; \
typedef __self::CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<int(__static_self_check_token) == int(_TySelf::__static_self_check_token)>)> __static_self_check
} // ~__self
Bu, benzersiz bir enum değeri üzerinde statik iddia (veya en azından tüm kodunuzu tek bir satıra yazmamanız durumunda benzersiz) yapar, tür karşılaştırma hilesi kullanılmaz ve şablonlarda bile statik iddia olarak çalışır. . Ve bonus olarak - son noktalı virgül artık gereklidir :).
Bana iyi bir ilham verdiği için Yakk'e teşekkür etmek isterim. Bunu önce cevabını görmeden yazmazdım.
VS 2008 ve g ++ 4.6.3 ile test edilmiştir. Nitekim XYZ
ve ABC
örnekle şikayet ediyor:
ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp:91:5: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â
self.cpp:91:5: error: template argument 1 is invalid
self.cpp: In function âvoid __self::__self_check_helper(_TyB*) [with _TyA = XYZ, _TyB = ABC]â:
self.cpp:91:5: instantiated from here
self.cpp:58:87: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<XYZ, ABC>â
Şimdi ABC'yi bir şablon yaparsak:
template <class X>
struct ABC {
DECLARE_SELF(XYZ); // line 92
};
int main(int argc, char **argv)
{
ABC<int> abc;
return 0;
}
Alacağız:
ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp: In instantiation of âABC<int>â:
self.cpp:97:18: instantiated from here
self.cpp:92:9: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â
İşlev kontrolü derlenmediğinden (beklendiği gibi) yalnızca satır numarası kontrolü tetiklendi.
C ++ 0x ile (ve kötü alt çizgiler olmadan), şunlara ihtiyacınız olacaktır:
namespace self_util {
/**
* @brief compile-time assertion (tokens in class and TySelf must match)
* @tparam b_check is the asserted value
*/
template <bool b_check>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;
/**
* @brief compile-time assertion (specialization for assertion passing)
*/
template <>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<true> {};
/**
* @brief static assertion helper type
* @tparam n_size is size of object being used as assertion message
* (if it's a incomplete type, compiler will display object name in error output)
*/
template <const size_t n_size>
class CStaticAssert {};
#define SELF_CHECK \
/** checks the consistency of TySelf type (calling it has no effect) */ \
void self_check() \
{ \
static_assert(std::is_same<TySelf, decltype(*this)>::value, "TySelf is not what it should be"); \
}
#define DECLARE_SELF(Type) \
typedef Type TySelf; /**< @brief type of this class */ \
SELF_CHECK \
enum { static_self_check_token = __LINE__ }; \
typedef self_util::CStaticAssert<sizeof(SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<int(static_self_check_token) == int(TySelf::static_self_check_token)>)> static_self_check
} // ~self_util
CStaticAssert bitinin, şablon gövdesinde yazılan bir tip ürettiği için maalesef hala gerekli olduğuna inanıyorum (sanırım aynısı ile yapılamaz static_assert
). Bu yaklaşımın avantajı hala sıfır maliyetidir.
this_t
muhtemelen normal C ++ adlandırma ile daha uyumlu olacaktır.