Tôi sẽ nhắc lại giải pháp hiển nhiên là "phải tự mình làm". Đây là phiên bản C ++ 11 ngắn gọn của mã, hoạt động với cả các lớp đơn giản và các mẫu lớp:
#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")
Bạn có thể thấy nó hoạt động tại Ideone . Nguồn gốc, dẫn đến kết quả này như sau:
#define DECLARE_SELF(Type) typedef Type _TySelf; /**< @brief type of this class */
struct XYZ {
DECLARE_SELF(XYZ)
};
Điều này có vấn đề rõ ràng là sao chép dán mã vào một lớp khác và quên thay đổi XYZ, như sau:
struct ABC {
DECLARE_SELF(XYZ) // !!
};
Cách tiếp cận đầu tiên của tôi không giống nguyên bản lắm - tạo một hàm, như thế này:
/**
* @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
Nó hơi dài dòng, nhưng hãy chịu đựng với tôi ở đây. Điều này có lợi thế khi làm việc trong C ++ 03 mà không cần decltype
, vì __self_check_helper
hàm được sử dụng để suy ra loại this
. Ngoài ra, không có static_assert
, nhưng sizeof()
thủ thuật được sử dụng để thay thế. Bạn có thể làm cho nó ngắn hơn nhiều đối với C ++ 0x. Bây giờ điều này sẽ không hoạt động đối với các mẫu. Ngoài ra, có một vấn đề nhỏ với macro không mong đợi dấu chấm phẩy ở cuối, nếu biên dịch với pedantic, nó sẽ phàn nàn về một dấu chấm phẩy thừa không cần thiết (hoặc bạn sẽ bị bỏ lại với một macro trông kỳ quặc không kết thúc bằng dấu chấm phẩy trong phần thân XYZ
và ABC
).
Thực hiện kiểm tra đối với những Type
gì được chuyển đến DECLARE_SELF
không phải là một tùy chọn, vì điều đó sẽ chỉ kiểm tra XYZ
lớp (tốt), không biết đến ABC
(có lỗi). Và sau đó nó đánh tôi. Một giải pháp không tốn thêm dung lượng lưu trữ hoạt động với các mẫu:
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
Điều này chỉ đơn giản là thực hiện xác nhận tĩnh trên một giá trị enum duy nhất (hoặc ít nhất là duy nhất trong trường hợp bạn không viết tất cả mã của mình trên một dòng), không có thủ thuật so sánh kiểu nào được sử dụng và nó hoạt động như xác nhận tĩnh, ngay cả trong các mẫu . Và như một phần thưởng - dấu chấm phẩy cuối cùng bây giờ là bắt buộc :).
Tôi muốn cảm ơn Yakk vì đã cho tôi một nguồn cảm hứng tốt. Tôi sẽ không viết điều này nếu lần đầu tiên nhìn thấy câu trả lời của anh ấy.
Đã thử nghiệm với VS 2008 và g ++ 4.6.3. Thật vậy, với ví dụ XYZ
và ABC
, nó phàn nàn:
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>â
Bây giờ nếu chúng ta tạo ABC thành một mẫu:
template <class X>
struct ABC {
DECLARE_SELF(XYZ); // line 92
};
int main(int argc, char **argv)
{
ABC<int> abc;
return 0;
}
Chúng ta sẽ lấy:
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>â
Chỉ kiểm tra số dòng được kích hoạt, vì kiểm tra chức năng không được biên dịch (như mong đợi).
Với C ++ 0x (và không có dấu gạch dưới ác), bạn chỉ cần:
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
Tôi tin rằng đáng tiếc là bit CStaticAssert vẫn được yêu cầu vì nó tạo ra một kiểu, được đánh máy trong phần thân mẫu (tôi cho rằng không thể thực hiện điều tương tự với static_assert
). Ưu điểm của phương pháp này là chi phí bằng không.
this_t
có lẽ sẽ phù hợp hơn với cách đặt tên C ++ thông thường.