diff options
author | cathook <b01902109@csie.ntu.edu.tw> | 2014-10-21 17:51:03 +0800 |
---|---|---|
committer | cathook <b01902109@csie.ntu.edu.tw> | 2014-10-21 17:51:03 +0800 |
commit | f7770eea2208dba6f3171adcb268f446263275cb (patch) | |
tree | 6723c6bb73610e74aebc41317eda423a5dbd617d | |
parent | bf5c9f2ae5c436aa7f59b28bb5e94b0362bfa358 (diff) | |
download | meow-f7770eea2208dba6f3171adcb268f446263275cb.tar meow-f7770eea2208dba6f3171adcb268f446263275cb.tar.gz meow-f7770eea2208dba6f3171adcb268f446263275cb.tar.bz2 meow-f7770eea2208dba6f3171adcb268f446263275cb.tar.lz meow-f7770eea2208dba6f3171adcb268f446263275cb.tar.xz meow-f7770eea2208dba6f3171adcb268f446263275cb.tar.zst meow-f7770eea2208dba6f3171adcb268f446263275cb.zip |
big change
40 files changed, 2798 insertions, 1479 deletions
diff --git a/meowpp/Makefile b/meowpp/Makefile index b087830..04ccf22 100644 --- a/meowpp/Makefile +++ b/meowpp/Makefile @@ -1,3 +1,4 @@ +MEOWPP_DIR ?= . CXX ?= g++ CXXFLAGS ?= -g -Wall -Werror -std=c++11 -pedantic @@ -5,7 +6,7 @@ CXXFLAGS ?= -g -Wall -Werror -std=c++11 -pedantic .PHONY: meowpp_test meowpp_test_clean meowpp_test: - meowpp/test.py -c '$(CXX) $(CXXFLAGS)' -p '$(CURRENT_DIR)' -P '$(MEOWPP_DIR)' + $(MEOWPP_DIR)/test.py -c '$(CXX) $(CXXFLAGS)' -p '$(MEOWPP_DIR)' -P '$(MEOWPP_DIR)' meowpp_test_clean: find '$(MEOWPP_DIR)' -regex '^.*\.bin$$' -exec rm {} \; diff --git a/meowpp/meow/alloc.h b/meowpp/meow/alloc.h new file mode 100644 index 0000000..59736bc --- /dev/null +++ b/meowpp/meow/alloc.h @@ -0,0 +1,203 @@ +/*! + * @file allocator.h + * @brief Supports a thread-safe allocator. + * + * @author cathook + */ +#ifndef __MEOW_ALLOCATOR_H__ +#define __MEOW_ALLOCATOR_H__ + +#include <memory> +#include <cstdlib> +#include <new> +#include <cstddef> + + +#ifndef MEOW_NO_THREADING_SAFE_MEMORY + +#include <meow/threading/locks.h> + +namespace meow { + +namespace { + +//! Lock for the new/delete operation. +RecurLock thread_safe_lock; + +} + +} + +#define MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE thread_safe_lock.Acquire() +#define MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE thread_safe_lock.Release() + +#else // MEOW_NO_THREADING_SAFE_MEMORY + +#define MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE +#define MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE + +#endif // MEOW_NO_THREADING_SAFE_MEMORY + +namespace meow { + +namespace { + +class ArrayBufferManager { + private: + + //! Stores the information about the array size. + struct ArrayBuffer { + //! Stores the size of the array who is at the address of buffer. + size_t num_of_entry; + size_t size_per_entry; + + //! Points to the buffer. + char buffer[1]; + }; + + /*! + * @brief Creates an ArrayBuffer with speicfied buffer size. + * @param [in] buffer_size Buffer size. + */ + static ArrayBuffer* CreateArrayBuffer(size_t buffer_size) { + return reinterpret_cast<ArrayBuffer*>( + malloc(sizeof(ArrayBuffer) + buffer_size)); + } + + /*! + * @brief Gets the address of the corrosponding instance of ArrayBuffer. + * @param [in] buffer Address of the buffer. + */ + static ArrayBuffer* GetArrayBufferFromBuffer(char* buffer) { + return reinterpret_cast<ArrayBuffer*>( + buffer - offsetof(ArrayBuffer, buffer)); + } + + public: + + /*! + * @brief Allocates an array. + * @param [in] size_per_entry Size per entry. + * @param [in] num_of_entry Array size. + * @return The address of the buffer for the allocated array. + */ + static char* Alloc(size_t size_per_entry, size_t num_of_entry) { + MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE; + ArrayBuffer* ptr = CreateArrayBuffer(size_per_entry * num_of_entry); + ptr->size_per_entry = size_per_entry; + ptr->num_of_entry = num_of_entry; + MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE; + return ptr->buffer; + } + + /*! + * @brief Gets the size of the array by the address of the buffer. + * @param [in] buffer address of the buffer. + */ + static size_t ArraySize(char* buffer) { + return GetArrayBufferFromBuffer(buffer)->num_of_entry; + } + + /*! + * @brief Gets the size of each entry by the address of the buffer. + * @param [in] buffer address of the buffer. + */ + static size_t EntrySize(char* buffer) { + return GetArrayBufferFromBuffer(buffer)->size_per_entry; + } + + /*! + * @brief Frees an array. + * @param [in] buffer address of the buffer. + */ + static void Free(char* buffer) { + MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE; + free(reinterpret_cast<void*>(GetArrayBufferFromBuffer(buffer))); + MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE; + } +}; + +} + + +/*! + * @brief News an element. + * @param [in] args Arguments for calling the constructor of the specified Type. + */ +template<typename Type, typename... Args> +inline Type* NewEntry(Args... args) { + MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE; + Type* ret = new Type(args...); + MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE; + return ret; +} + + +/*! + * @brief Deletes an element. + */ +template<typename Type> +inline Type* DeleteEntry(Type* obj) { + MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE; + delete obj; + MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE; + return NULL; +} + + +/*! + * @brief News an array of elements. + * @param [in] args Arguments for calling the constructor of the specified Type. + */ +template<typename Type, typename... Args> +inline Type* NewArray(size_t size, Args... args) { + Type* ret = reinterpret_cast<Type*>( + ArrayBufferManager::Alloc(sizeof(Type), size)); + for (size_t i = 0; i < size; ++i) { + new(ret + i) Type(args...); + } + return ret; +} + + +/*! + * @brief Deletes an array. + */ +template<typename Type> +inline Type* DeleteArray(Type* obj) { + char* buffer = reinterpret_cast<char*>(obj); + size_t array_len = ArrayBufferManager::ArraySize(buffer); + size_t entry_size = ArrayBufferManager::EntrySize(buffer); + for (size_t i = 0; i < array_len; ++i) { + reinterpret_cast<Type*>(buffer + i * entry_size)->~Type(); + } + ArrayBufferManager::Free(reinterpret_cast<char*>(obj)); + return NULL; +} + + +/*! + * @brief Thread-safe allocator. + */ +template<typename Type> +class Alloc : public std::allocator<Type> { + public: + virtual ~Alloc() {} + + Type* allocate(std::size_t n, const void* hint = NULL) { + MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE; + std::allocator<Type>::allocate(n, hint); + MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE; + } + + void deallocate(Type* p, std::size_t n) { + MEOW_ALLOC_THREAD_SAFE_LOCK_ACQUIRE; + std::allocator<Type>::deallocate(n); + MEOW_ALLOC_THREAD_SAFE_LOCK_RELEASE; + } +}; + + +} + +#endif // __MEOW_ALLOCATOR_H__ diff --git a/meowpp/meow/alloc_test.cpp b/meowpp/meow/alloc_test.cpp new file mode 100644 index 0000000..90e6faa --- /dev/null +++ b/meowpp/meow/alloc_test.cpp @@ -0,0 +1,66 @@ +#include <cstdio> +#include <cmath> +#include <chrono> + +#include <meow/syntax.h> +#include <meow/alloc.h> +#include <meow/threading/thread.h> + +class LongInit { + public: + void hi() {} + LongInit(int t) { + std::this_thread::sleep_for(std::chrono::seconds(t)); + } + LongInit() : LongInit(1) {} +}; + +class T1 : public meow::Thread { + private: + void Run() { + LongInit* a = meow::NewEntry<LongInit>(3); + a->hi(); + Assert(meow::DeleteEntry(a) == NULL, "delete not null"); + } +}; + +class T2 : public meow::Thread { + private: + void Run() { + LongInit* a = meow::NewArray<LongInit>(5); + a->hi(); + meow::DeleteArray(a); + } +}; + +class Obj { + private: + int* flag_; + public: + Obj(int* flag) : flag_(flag) { (*flag_) += 1; } + ~Obj() { (*flag_) -= 1; } +}; + +int main() { + T1 t1; + T2 t2; + std::chrono::system_clock::time_point tp1 = std::chrono::system_clock::now(); + t1.Start(); + t2.Start(); + t1.Join(); + t2.Join(); + std::chrono::system_clock::time_point tp2 = std::chrono::system_clock::now(); + long int k = std::chrono::duration_cast<std::chrono::microseconds>( + tp2 - tp1).count(); + Assert(fabs(k / 1e6 - 8) < 5e-2, "Time cost error = %f / 8.0", k / 1e6); + + with (int flag = 0) { + with (Obj* obj_ptr = meow::NewArray<Obj>(3, &flag)) { + Assert(flag == 3, "array no construct... (%d)", flag); + obj_ptr = meow::DeleteArray(obj_ptr); + Assert(flag == 0, "array no destruct..."); + Assert(obj_ptr == NULL, "array no destruct...(%d)", flag); + } + } + return 0; +} diff --git a/meowpp/utility/basetypes.h b/meowpp/meow/bases/basetypes.h index 2efd64e..3326caf 100644 --- a/meowpp/utility/basetypes.h +++ b/meowpp/meow/bases/basetypes.h @@ -1,10 +1,11 @@ -#ifndef __MEOWPP_UTILITY_BASETYPES_H__ -#define __MEOWPP_UTILITY_BASETYPES_H__ +#ifndef __MEOW_BASES_BASETYPES_H__ +#define __MEOW_BASES_BASETYPES_H__ #include <cstdint> -#include "object.h" -#include "../debug/assert.h" +#include <meow/alloc.h> +#include <meow/bases/object.h> +#include <meow/debug/assert.h> namespace meow { @@ -23,16 +24,8 @@ class BaseNumberType : public Object { public: BaseNumberType() : BaseNumberType(static_cast<DataType>(0)) {} BaseNumberType(BaseNumberType const& b) : BaseNumberType(b.value_) {} - BaseNumberType(DataType const& arg_init_value) : value_(arg_init_value) {} + BaseNumberType(DataType const& value) : value_(value) {} ~BaseNumberType() {} - Object* Copy() const { return new BaseNumberType(value_); } - Object* CopyFrom(Object const* ptr) { - value_ = static_cast<BaseNumberType const*>(ptr)->value_; - return this; - } - bool Equals(Object const* ptr) const { - return (value_ == static_cast<BaseNumberType const*>(ptr)->value_); - } operator DataType() const { return value_; } BaseNumberType& operator=(DataType const& b) { value_ = b; return *this; } BaseNumberType& operator+=(DataType const& b) { value_ += b; return *this; } @@ -44,6 +37,16 @@ class BaseNumberType : public Object { BaseNumberType& operator^=(DataType const& b) { value_ ^= b; return *this; } BaseNumberType& operator<<=(int64_t const& b) { value_ <<= b; return *this; } BaseNumberType& operator>>=(int64_t const& b) { value_ >>= b; return *this; } + BaseNumberType& CopyFrom(BaseNumberType const& b) { + return operator=(b.value_); + } + bool Equals(BaseNumberType const& b) const { return value_ == b.value_; } + + //! Settings for Object. + SET_MEOW_OBJECT_COPYABLE(true); + IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(BaseNumberType); + DELEGATE_MEOW_OBJECT_COPYFROM(BaseNumberType); + DELEGATE_MEOW_OBJECT_EQUALS(BaseNumberType); }; @@ -61,20 +64,22 @@ class BaseFloatingType : public Object { BaseFloatingType(BaseFloatingType const& b) : BaseFloatingType(b.value_) {} BaseFloatingType(DataType const& arg_init_value) : value_(arg_init_value) {} ~BaseFloatingType() {} - Object* Copy() const { return new BaseFloatingType(value_); } - Object* CopyFrom(Object const* ptr) { - value_ = static_cast<BaseFloatingType const*>(ptr)->value_; - return this; - } - bool Equals(Object const* ptr) const { - return (value_ == static_cast<BaseFloatingType const*>(ptr)->value_); - } operator DataType() const { return value_; } BaseFloatingType& operator=(DataType const& b) { value_ = b; return *this; } BaseFloatingType& operator+=(DataType const& b) { value_ += b; return *this; } BaseFloatingType& operator-=(DataType const& b) { value_ -= b; return *this; } BaseFloatingType& operator*=(DataType const& b) { value_ *= b; return *this; } BaseFloatingType& operator/=(DataType const& b) { value_ /= b; return *this; } + BaseFloatingType& CopyFrom(BaseFloatingType const& b) { + return operator=(b.value_); + } + bool Equals(BaseFloatingType const& b) const { return value_ == b.value_; } + + //! Settings for Object. + SET_MEOW_OBJECT_COPYABLE(true); + IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(BaseFloatingType); + DELEGATE_MEOW_OBJECT_COPYFROM(BaseFloatingType); + DELEGATE_MEOW_OBJECT_EQUALS(BaseFloatingType); }; } @@ -95,4 +100,4 @@ typedef BaseFloatingType<long double> LDouble; //!< Long double. } // meow -#endif // __MEOWPP_UTILITY_BASETYPES_H__ +#endif // __MEOW_BASES_BASETYPES_H__ diff --git a/meowpp/utility/basetypes_test.cpp b/meowpp/meow/bases/basetypes_test.cpp index 1a7c076..a210e8b 100644 --- a/meowpp/utility/basetypes_test.cpp +++ b/meowpp/meow/bases/basetypes_test.cpp @@ -1,7 +1,8 @@ -#include <meowpp/utility/basetypes.h> +#include <meow/bases/basetypes.h> int main() { meow::Int32 a; + Assert(a.copyable(), ""); Assert(a == 0, ""); Assert(0 == a, ""); Assert(!a, ""); @@ -24,17 +25,22 @@ int main() { Assert((~a) == ~10, ""); a += 3; Assert(a == 13, ""); + a -= 3; + Assert(a == 10, ""); + a <<= 1; + Assert(a == 20, ""); + a >>= 2; + Assert(a == 5, ""); meow::Double b(a); - Assert(b == 13.0, ""); - Assert(b == 13, ""); - meow::Double* k = static_cast<meow::Double*>(b.Copy()); + Assert(b != 13.0, ""); + meow::Double* k = dynamic_cast<meow::Double*>(b.Copy()); Assert((*k) == b, ""); Assert(k->Equals(&b), ""); (*k) = 5; - Assert((*k) == 5, ""); + Assert((*k) == 5.0, ""); Assert(k->CopyFrom(&b) == k, ""); - Assert((*k) == 13, ""); - delete k; + Assert((*k) != 13, ""); + DeleteEntry(k); return 0; } diff --git a/meowpp/meow/bases/object.h b/meowpp/meow/bases/object.h new file mode 100644 index 0000000..7953cd5 --- /dev/null +++ b/meowpp/meow/bases/object.h @@ -0,0 +1,126 @@ +/*! + * @file object.h + * @brief Contains a base class for most of all the classes in meowpp. + * + * @author cathook + */ + +#ifndef __MEOW_BASES_OBJECT_H__ +#define __MEOW_BASES_OBJECT_H__ + +#include <cstdlib> + +namespace meow { + + +/*! + * @brief The base class. + */ +class Object { + protected: + + /*! + * @brief A protected constructor to prevent developers create an instance of + * Object directly. + */ + Object() {} + + /*! + * @brief Disable the copy operation. + */ + Object(Object const& b); + + public: + + /*! + * @brief Virtual destructor. + */ + virtual ~Object() {} + + /*! + * @brief Returns whether this object is an non-copyable type or not. + * @return false + * + * Default is no. + */ + virtual bool copyable() const { + return false; + } + + /*! + * @brief A simple way for user to setup whether the class is copyable or not. + * @param [in] x true or false. + */ +#define SET_MEOW_OBJECT_COPYABLE(x) bool copyable() const { return x; } + + /*! + * @brief Creates a copy of itself and return the pointer to it. + * + * It will return NULL in default if you don't implement it. + */ + virtual Object* Copy() const { + return NULL; + } + + /*! + * @brief A simple way for user to implement the method "Copy". + * @param [in] X The class name. + * + * By calling the copy constructor. + */ +#define IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(X) \ + Object* Copy() const { \ + return dynamic_cast<Object*>(NewEntry<X>(*this)); \ + } + + /*! + * @brief Copies data from another object. + * @param [in] ptr Points to another object. + * + * It will return NULL in default if you don't implement it. + */ + virtual Object* CopyFrom(Object const* ptr) { + return NULL; + } + + /*! + * @brief A simple way for user to implement the method "CopyFrom". + * @param [in] X The class name. + * + * By calling the CopyFrom method in the user defined class. + */ +#define DELEGATE_MEOW_OBJECT_COPYFROM(X) \ + Object* CopyFrom(Object const* b) { \ + return dynamic_cast<Object*>(&CopyFrom(*dynamic_cast<X const*>(b))); \ + } + + /*! + * @brief Returns whether it equals to another object or not. + * @param [in] ptr Points to another object. + * + * It will always return false if you don't implement it. + */ + virtual bool Equals(Object const* ptr) const { + return false; + } + + /*! + * @brief A simple way for user to implement the method "Equals". + * @param [in] X The class name. + * + * By calling the Equals method in the user defined class. + */ +#define DELEGATE_MEOW_OBJECT_EQUALS(X) \ + bool Equals(Object const* b) const { \ + return Equals(*dynamic_cast<X const*>(b)); \ + } + + /*! + * @brief Disable the copy operator. + */ + Object& operator=(Object const& b); +}; + +} // meow + +#endif // __MEOW_BASES_OBJECT_H__ diff --git a/meowpp/utility/object_test.cpp b/meowpp/meow/bases/object_test.cpp index 5c60440..1dfbe77 100644 --- a/meowpp/utility/object_test.cpp +++ b/meowpp/meow/bases/object_test.cpp @@ -1,7 +1,7 @@ -#include <meowpp/debug/assert.h> -#include <meowpp/utility/object.h> -#include <meowpp/utility/object.h> -#include <meowpp/utility/object.h> +#include <meow/debug/assert.h> +#include <meow/bases/object.h> +#include <meow/bases/object.h> +#include <meow/bases/object.h> static bool destructor_be_called = false; diff --git a/meowpp/utility/operation.h b/meowpp/meow/bases/operation.h index d595668..3685459 100644 --- a/meowpp/utility/operation.h +++ b/meowpp/meow/bases/operation.h @@ -5,13 +5,13 @@ * @author cathook */ -#ifndef __MEOWPP_UTILITY_OPERATION_H__ -#define __MEOWPP_UTILITY_OPERATION_H__ +#ifndef __MEOW_BASES_OPERATION_H__ +#define __MEOW_BASES_OPERATION_H__ -#include "../debug/assert.h" -#include "object.h" -#include "pointer.h" -#include "state.h" +#include <meow/debug/assert.h> +#include <meow/bases/object.h> +#include <meow/bases/pointers.h> +#include <meow/bases/state.h> namespace meow { @@ -50,8 +50,8 @@ class Operation : public Object { * to the output elements. * @return The state of the operation (ex: fail, success, ...) */ - virtual State Operate(Pointer<Object const> const * inputs_ptr, - Pointer<Object> const * outputs_ptr) const = 0; + virtual State Operate(ArrayPointer<Object const> const * inputs_ptr, + ArrayPointer<Object> const * outputs_ptr) const = 0; /*! * @brief Gets the number of inputs for the operation. @@ -69,12 +69,12 @@ class Operation : public Object { return outputs_size_; } -#ifdef MEOWPP_UTILITY_OPERATION_TESTING +#ifdef MEOW_BASES_OPERATION_TESTING friend class OperationTest; -#endif // MEOWPP_UTILITY_OPERATION_TESTING +#endif // MEOW_BASES_OPERATION_TESTING }; } // meow -#endif // __MEOWPP_UTILITY_OPERATION_H__ +#endif // __MEOW_BASES_OPERATION_H__ diff --git a/meowpp/utility/operation_test.cpp b/meowpp/meow/bases/operation_test.cpp index def8d84..4587793 100644 --- a/meowpp/utility/operation_test.cpp +++ b/meowpp/meow/bases/operation_test.cpp @@ -1,10 +1,10 @@ -#include <meowpp/utility/operation.h> -#include <meowpp/utility/operation.h> -#include <meowpp/utility/operation.h> +#define MEOW_BASES_OPERATION_TESTING -#include <meowpp/debug/assert.h> +#include <meow/bases/operation.h> +#include <meow/debug/assert.h> namespace meow { + class OperationTest { private: class States : public State { @@ -17,16 +17,16 @@ class OperationTest { int id; Oper1(int k) : Operation(3, 5), id(k) {} Object* Copy() const { return new Oper1(id); } - State Operate(Pointer<Object const> const * inputs, - Pointer<Object> const* outputs) const { + State Operate(ArrayPointer<Object const> const * inputs, + ArrayPointer<Object> const* outputs) const { return States::CASE1; } }; class Oper2 : public Operation { public: Oper2() : Operation(2, 7) {} - State Operate(Pointer<Object const> const * inputs, - Pointer<Object> const* outputs) const { + State Operate(ArrayPointer<Object const> const * inputs, + ArrayPointer<Object> const* outputs) const { return States::CASE2; } }; diff --git a/meowpp/meow/bases/pointers.array_pointer_test.cpp b/meowpp/meow/bases/pointers.array_pointer_test.cpp new file mode 100644 index 0000000..5c3495a --- /dev/null +++ b/meowpp/meow/bases/pointers.array_pointer_test.cpp @@ -0,0 +1,79 @@ +#define MEOW_POINTER_TEST + +#include <meow/bases/pointers.h> +#include <meow/syntax.h> + +#include <cstdio> + +using namespace meow; + +int global_counter = 0; + +class ElementBase { + public: + virtual ~ElementBase() {} +}; + +class Element : public ElementBase { + private: + int* counter_; + public: + Element() : counter_(&global_counter) { (*counter_) += 1; } + Element(int* ct) : counter_(ct) { (*counter_) += 1; } + ~Element() { (*counter_) -= 1; } + void hi() { fprintf(stderr, "hi!!\n"); } +}; + +int main() { + with (ArrayPointer<Element> ap(5)) { + Assert(global_counter == 5, ""); + Assert(ap.address() != NULL, ""); + Assert(ap.counter() == 1, ""); + Assert(ap.begin_index() == 0, ""); + Assert(ap.end_index() == 5, ""); + } + Assert(global_counter == 0, "Not destructor?"); + with (int counter = 0) { + with (ArrayPointer<ElementBase> apb) { + Assert(apb.address() == NULL, ""); + with (ArrayPointer<Element> ap(7, &counter)) { + Assert(counter == 7, ""); + with (ArrayPointer<Element> ap2(ap)) { + Assert(counter == 7, ""); + Assert(ap.address() == ap2.address(), ""); + Assert(ap.ptr_counter() == ap2.ptr_counter(), ""); + Assert(ap.counter() == 2, ""); + Assert(ap2.begin_index() == 0, ""); + Assert(ap2.end_index() == 7, ""); + ap2 += 2; + Assert(ap.counter() == 2, ""); + Assert(ap2.begin_index() == -2, ""); + Assert(ap2.end_index() == 5, ""); + ap2 -= 3; + Assert(ap2.begin_index() == 1, ""); + Assert(ap2.end_index() == 8, ""); + with (ArrayPointer<Element> ap3(ap2 + 3)) { + Assert(ap3.counter() == 3, ""); + Assert(ap3.begin_index() == -2, ""); + Assert(ap3.end_index() == 5, ""); + for (int i = -2; i < 5; ++i) { + ap3[i].hi(); + } + } + Assert(ap.counter() == 2, ""); + } + Assert(counter == 7, ""); + Assert(ap.counter() == 1, ""); + apb = (ArrayPointer<ElementBase>)ap; + Assert(ap.ptr_counter() == apb.ptr_counter(), ""); + Assert(ap.counter() == 2, ""); + } + Assert(counter == 7, ""); + Assert(apb.counter() == 1, ""); + fprintf(stderr, "goout111\n"); + } + fprintf(stderr, "goout222\n"); + Assert(counter == 0, ""); + } + return 0; +} diff --git a/meowpp/meow/bases/pointers.entry_pointer_test.cpp b/meowpp/meow/bases/pointers.entry_pointer_test.cpp new file mode 100644 index 0000000..a0ce772 --- /dev/null +++ b/meowpp/meow/bases/pointers.entry_pointer_test.cpp @@ -0,0 +1,83 @@ +#define MEOW_POINTER_TEST + +#include <meow/syntax.h> +#include <meow/bases/pointers.h> + +#include <cstdio> + +using namespace meow; + +int global_counter = 0; + +class ElementBase { + public: + virtual ~ElementBase() {} +}; + +class Element : public ElementBase { + private: + int* counter_; + public: + Element() : counter_(&global_counter) { (*counter_) += 1; } + Element(int* ct) : counter_(ct) { (*counter_) += 1; } + ~Element() { (*counter_) -= 1; } + void hi() { fprintf(stderr, "hi!!\n"); } +}; + +int main() { + with (EntryPointer<Element> ap(EPCreateFlag)) { + Assert(global_counter == 1, ""); + Assert(ap.address() != NULL, ""); + Assert(ap.counter() == 1, ""); + Assert(ap.is_entry_of_array() == false, ""); + } + Assert(global_counter == 0, "Not destructor?"); + with (int counter = 0) { + with (EntryPointer<ElementBase> apb) { + Assert(apb.address() == NULL, ""); + Assert(apb.is_entry_of_array() == false, ""); + with (EntryPointer<Element> ap(EPCreateFlag, &counter)) { + Assert(counter == 1, ""); + Assert(ap.is_entry_of_array() == false, ""); + with (EntryPointer<Element> ap2(ap)) { + Assert(counter == 1, ""); + Assert(ap.address() == ap2.address(), ""); + Assert(ap.ptr_counter() == ap2.ptr_counter(), ""); + Assert(ap.counter() == 2, ""); + with (EntryPointer<Element> ap3(ap2)) { + Assert(ap3.counter() == 3, ""); + Assert(ap3.is_entry_of_array() == false, ""); + ap3->hi(); + } + Assert(ap.counter() == 2, ""); + } + Assert(counter == 1, ""); + Assert(ap.counter() == 1, ""); + apb = (EntryPointer<ElementBase>)ap; + Assert(ap.ptr_counter() == apb.ptr_counter(), ""); + Assert(ap.counter() == 2, ""); + } + Assert(counter == 1, ""); + Assert(apb.counter() == 1, ""); + fprintf(stderr, "goout111\n"); + } + fprintf(stderr, "goout222\n"); + Assert(counter == 0, ""); + } + EntryPointer<int> xx; + with (ArrayPointer<int> kk(3, 5)) { + xx = kk + 2; + (*xx) = 1; + for (int i = 0; i < 3; ++i) { + if (i == 2) { + Assert(kk[i] == 1, ""); + } else { + Assert(kk[i] == 5, ""); + } + } + Assert(kk.counter() == 2, ""); + Assert(kk.ptr_counter() == xx.ptr_counter(), ""); + } + Assert(xx.counter() == 1, ""); + return 0; +} diff --git a/meowpp/meow/bases/pointers.h b/meowpp/meow/bases/pointers.h new file mode 100644 index 0000000..9305bcf --- /dev/null +++ b/meowpp/meow/bases/pointers.h @@ -0,0 +1,842 @@ +/*! + * @file pointer.h + * @brief Contains a pointer class which has a counter-mechanism to prevent + * memory leak. + * + * @author cathook + */ + +#ifndef __MEOW_BASES_POINTER_H__ +#define __MEOW_BASES_POINTER_H__ + +#include <cstddef> +#include <cstdint> +#include <cstdlib> + +#include <meow/bases/object.h> +#include <meow/debug/assert.h> +#include <meow/alloc.h> + +namespace meow { + +/*! + * @brief A normal pointer which is a child of the base class Object. + */ +template<typename Type> +class WeakPointer : public Object { + private: + Type* address_; + + public: + /*! + * @brief Points to NULL. + */ + WeakPointer() : WeakPointer(NULL) {} + + /*! + * @brief Points to the address pointed by the specified pointer. + */ + WeakPointer(WeakPointer const & ptr2) : WeakPointer(address()) {} + + /*! + * @brief Points to the specified address. + */ + WeakPointer(Type* addr) : address_(addr) {} + + /*! + * @brief Destructor. + * @note It will not delete/free the memory! + */ + ~WeakPointer() {} + + /*! + * @brief Returns the address it points to. + */ + Type* address() const { return address_; } + + /*! + * @brief Returns the address it points to. + */ + operator Type*() const { return address(); } + + /*! + * @brief Returns the address it points to. + */ + Type* operator->() const { return address(); } + + /*! + * @brief Returns the reference of the address it points to. + */ + Type& operator*() const { return *address(); } + + /*! + * @brief Returns an positive offseted pointer of myself. + * @param [in] diff number of offset. + */ + WeakPointer operator+(ptrdiff_t const & diff) const { + return WeakPointer(address() + diff); + } + + /*! + * @brief Returns an negative offseted pointer of myself. + * @param [in] diff number of offset. + */ + WeakPointer operator-(ptrdiff_t const & diff) const { + return operator+(-diff); + } + + /*! + * @brief Offsets myself positively and returns *this. + * @param [in] diff number of offset. + */ + WeakPointer& operator+=(ptrdiff_t const & diff) { + address_ += diff; + return *this; + } + + /*! + * @brief Offsets myself negitavely and returns *this. + * @param [in] diff number of offset. + */ + WeakPointer& operator-=(ptrdiff_t const & diff) { + return operator+=(-diff); + } + + /*! + * @brief Returns the distance between another WeakPointer. + * @param [in] t Another WeakPointer. + */ + ptrdiff_t operator-(WeakPointer const & t) const { + return address() - t.address(); + } + + /*! + * @brief Assign operator. + */ + WeakPointer& operator=(WeakPointer const & t) { return CopyFrom(t); } + + /*! + * @brief Assign method. + */ + WeakPointer& CopyFrom(WeakPointer const & t) { + address_ = t.address_; + return *this; + } + + /*! + * @brief Is-equal operator. + */ + bool operator==(WeakPointer const & t) const { return Equals(t); } + + /*! + * @brief Is-equal method. + * @brief true if the addresses pointed to are same. + */ + bool Equals(WeakPointer const & t) const { return address() == t.address(); } + + /*! + * @brief Less-than operator. + */ + bool operator<(WeakPointer const & t) const { return LessThan(t); } + + /*! + * @brief Less-than method. + * @brief true if my address is less than the other address. + */ + bool LessThan(WeakPointer const & t) const { + return address() < t.address(); + } + + //! Settings for Object. + SET_MEOW_OBJECT_COPYABLE(true); + IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(WeakPointer); + DELEGATE_MEOW_OBJECT_COPYFROM(WeakPointer); + DELEGATE_MEOW_OBJECT_EQUALS(WeakPointer); +}; + +namespace { + +/*! + * @brief For the data shared by difference type of Pointer. + */ +class PointerTypesShareCounter { + protected: + size_t* counter_; + + public: + friend size_t* GetPointerCounter(PointerTypesShareCounter const* ptr); +}; + +size_t* GetPointerCounter(PointerTypesShareCounter const* ptr) { + return ptr->counter_; +} + +} + + +/*! + * @brief For pointing to an array. + */ +template<typename Type> +class ArrayPointer : public Object, public PointerTypesShareCounter { + private: + + //! Address of the array. + Type* address_; + + //! The first valid index. + ssize_t begin_index_; + + //! The next one of the last valid index. + ssize_t end_index_; + + template<typename Type2> + void Attach_(ArrayPointer<Type2> const & ptr2) { + address_ = reinterpret_cast<Type*>(ptr2.address()); + counter_ = GetPointerCounter( + dynamic_cast<PointerTypesShareCounter const*>(&ptr2)); + (*counter_) += 1; + begin_index_ = ptr2.begin_index(); + end_index_ = ptr2.end_index(); + } + + void Detach_() { + (*counter_) -= 1; + if (*counter_ == 0) { + address_ += reinterpret_cast<ptrdiff_t>(begin_index_); + if (address_ != NULL) + DeleteArray(address_); + DeleteEntry(counter_); + } + } + + ArrayPointer(Type* addr, size_t len) : + address_(addr), + begin_index_(0), + end_index_(len) { + counter_ = NewEntry<size_t>(1); + } + + template<typename Type2> + ArrayPointer(ArrayPointer<Type2> const & ptr2, ssize_t offset) { + Attach_(ptr2); + Offset(offset); + } + + public: + + /*! + * @brief Points to NULL. + */ + ArrayPointer() : ArrayPointer(reinterpret_cast<Type*>(NULL), 0) {} + + /*! + * @brief Points to another array with same type. + * @param [in] ptr2 Pointer of another array. + */ + ArrayPointer(ArrayPointer const & ptr2) : + ArrayPointer(ptr2, 0) {} + + /*! + * @brief Points to another array. + * @param [in] ptr2 Pointer of another array. + */ + template<typename Type2> + ArrayPointer(ArrayPointer<Type2> const & ptr2) : + ArrayPointer(ptr2, 0) {} + + /*! + * @brief Creates an array and then points to it. + * @param [in] len Size of the array. + * @param [in] args Arguments of the constructor of the Type. + */ + template<typename... Args> + ArrayPointer(size_t len, Args... args) : + ArrayPointer(NewArray<Type>(len, args...), len) {} + + /*! + * @brief Desturctor. + * + * It will automatically delete the array if nobody points to that array after + * itself be destroyed. + */ + ~ArrayPointer() { Detach_(); } + + /*! + * @brief Gets the address of the array. + */ + Type* address() const { return address_; } + + /*! + * @brief Returns the number of pointer points to it. + */ + size_t counter() const { return *counter_; } + +#ifdef MEOW_POINTER_TEST + /*! + * @brief For testing. + */ + size_t* ptr_counter() const { return counter_; } +#endif // MEOW_POINTER_TEST + + /*! + * @brief Returns the smallest valid index. + */ + ssize_t begin_index() const { return begin_index_; } + + /*! + * @brief Returns the one after the largest valid index. + */ + ssize_t end_index() const { return end_index_; } + + /*! + * @brief Offsets a specified number. + * @param [in] offs Offset mount. + */ + ArrayPointer& Offset(ssize_t offs) { + address_ += reinterpret_cast<ptrdiff_t>(offs); + begin_index_ -= offs; + end_index_ -= offs; + return *this; + } + + /*! + * @brief Returns the array. + */ + operator Type*() const { return address(); } + + /*! + * @brief Transformates to WeakPointer. + */ + operator WeakPointer<Type>() const { return WeakPointer<Type>(address()); } + + /*! + * @brief Transformates to another type. + */ + template<typename Type2> + operator ArrayPointer<Type2>() const { return ArrayPointer<Type2>(*this); } + + /*! + * @brief Gets the address of the first element of the array. + */ + Type* operator->() const { return address(); } + + /*! + * @brief Gets the reference of the first element of the array. + */ + Type& operator*() const { return *address(); } + + /*! + * @brief Gets the reference of the i-th element of the array. + * @param [in] index Index of the element. + */ + Type& operator[](ssize_t index) const { + Assert(begin_index_ <= index && index < end_index_, + "Index %ld is not in the valid range [%ld, %ld)", + index, begin_index_, end_index_); + return *(address() + reinterpret_cast<ptrdiff_t>(index)); + } + + /*! + * @brief Returns an array after offset positively from itself. + * @param [in] diff Offset. + */ + ArrayPointer operator+(ptrdiff_t const & diff) const { + return ArrayPointer(*this, reinterpret_cast<ssize_t>(diff)); + } + + + /*! + * @brief Returns an array after offset negatively from itself. + * @param [in] diff Offset. + */ + ArrayPointer operator-(ptrdiff_t const & diff) const { + return operator+(-diff); + } + + /*! + * @brief Offsets itself positively. + * @param [in] diff Offset. + */ + ArrayPointer& operator+=(ptrdiff_t const & diff) { + return Offset(reinterpret_cast<ssize_t>(diff)); + } + + /*! + * @brief Offsets itself negatively. + * @param [in] diff Offset. + */ + ArrayPointer& operator-=(ptrdiff_t const & diff) { + return operator+=(-diff); + } + + /*! + * @brief Gets the offset between itself and a WeakPointer. + * @param [in] t The instance of WeakPointer. + */ + ptrdiff_t operator-(WeakPointer<Type> const & t) const { + return address() - t.address(); + } + + + /*! + * @brief Points to another array. + * @param [in] t Another array. + */ + ArrayPointer& operator=(ArrayPointer const & t) { + return CopyFrom(t); + } + + /*! + * @brief Points to another array. + * @param [in] t Another array. + */ + ArrayPointer& CopyFrom(ArrayPointer const & t) { + Detach_(); + Attach_(t); + return *this; + } + + /*! + * @brief Return whether them are points to the same address or not. + * @param [in] t Another array. + */ + bool operator==(ArrayPointer const & t) const { + return Equals(t); + } + + /*! + * @brief Return whether them are points to the same address or not. + * @param [in] t Another array. + */ + bool Equals(ArrayPointer const & t) const { + return address() == t.address(); + } + + /*! + * @brief Less-than operator. + */ + bool operator<(ArrayPointer const & t) const { return LessThan(t); } + + /*! + * @brief Less-than method. + * @brief true if my address is less than the other address. + */ + bool LessThan(ArrayPointer const & t) const { + return address() < t.address(); + } + + //! Settings for Object. + SET_MEOW_OBJECT_COPYABLE(true); + IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(ArrayPointer); + DELEGATE_MEOW_OBJECT_COPYFROM(ArrayPointer); + DELEGATE_MEOW_OBJECT_EQUALS(ArrayPointer); +}; + + +namespace { + +/*! + * @brief Base class for information in types of EntryPointer. + * + * Because the EntryPointer may be an element of an array or not, we need to + * handle each case seperately. + */ +template<typename Type> +class EntryPointerBase : public Object { + public: + + /*! + * @brief Destructor. + */ + virtual ~EntryPointerBase() {} + + /*! + * @brief Gets the address. + */ + virtual Type* address() const = 0; + + /*! + * @brief Gets the number of pointer points to the address. + */ + virtual size_t counter() const = 0; + +#ifdef MEOW_POINTER_TEST + /*! + * @brief For testing. + */ + virtual size_t* ptr_counter() const = 0; +#endif // MEOW_POINTER_TEST + + /*! + * @brief Returns whether it is an element in an array or not. + */ + virtual bool is_array_pointer() const = 0; + + /*! + * @brief Copies from another EntryPointerBase. + */ + virtual void CopyFrom(EntryPointerBase const *) = 0; + + /*! + * @brief Creates and returns a copy of itself. + */ + virtual EntryPointerBase* Copy() const = 0; +}; + +/*! + * @brief For the case that the address pointed to is an element of an array. + */ +template<typename Type> +class ArrayEntryPointer : public EntryPointerBase<Type> { + private: + + //! Stores the pointer points to the array. + ArrayPointer<Type> ap_; + + public: + ArrayEntryPointer(ArrayPointer<Type> const & ap) : ap_(ap) {} + + template<typename Type2> + ArrayEntryPointer(ArrayPointer<Type2> const & ap) : ap_(ap) {} + + ArrayPointer<Type> array_pointer() const { return ap_; } + + Type* address() const { return ap_.address(); } + size_t counter() const { return ap_.counter(); } +#ifdef MEOW_POINTER_TEST + size_t* ptr_counter() const { return ap_.ptr_counter(); } +#endif // MEOW_POINTER_TEST + bool is_array_pointer() const { return true; } + void CopyFrom(EntryPointerBase<Type> const * epb2) { + ap_.CopyFrom(dynamic_cast<ArrayEntryPointer const *>(epb2)->ap_); + } + EntryPointerBase<Type>* Copy() const { + return dynamic_cast<EntryPointerBase<Type>*>( + NewEntry<ArrayEntryPointer>(ap_)); + } +}; + +/*! + * @brief For the case that the address is not an element of an array. + */ +template<typename Type> +class NormalEntryPointer : + public EntryPointerBase<Type>, public PointerTypesShareCounter { + private: + + //! Points to the address. + Type* address_; + + template<typename Type2> + void Attach_(NormalEntryPointer<Type2> const & t) { + address_ = reinterpret_cast<Type*>(t.address()); + counter_ = GetPointerCounter( + dynamic_cast<PointerTypesShareCounter const*>(&t)); + (*counter_) += 1; + } + + void Detach_() { + (*counter_) -= 1; + if (*counter_ <= 0) { + DeleteEntry(address_); + DeleteEntry(counter_); + } + } + + NormalEntryPointer(Type* addr) : address_(addr) { + counter_ = NewEntry<size_t>(1); + } + public: + struct CREATE_OBJ {}; + + /*! + * @brief Points to NULL + */ + NormalEntryPointer() : NormalEntryPointer(NULL) {} + + /*! + * @brief Points to the address pointed by the specified pointer. + * @param [in] t The pointer. + */ + NormalEntryPointer(NormalEntryPointer const & t) { + Attach_(t); + } + + /*! + * @brief Points to the address pointed by the specified pointer with + * differnet type. + * @param [in] t The pointer. + */ + template<typename Type2> + NormalEntryPointer(NormalEntryPointer<Type2> const & t) { + Attach_(t); + } + + /*! + * @brief Creates an instance of Type and points to it. + * @param [in] args Arguments for the constructor of the Type. + */ + template<typename... Args> + NormalEntryPointer(CREATE_OBJ, Args... args) : + NormalEntryPointer(NewEntry<Type>(args...)) {} + + + /*! + * @brief Desturctor. + * + * It will automatically delete the array if nobody points to that array after + * itself be destroyed. + */ + ~NormalEntryPointer() { Detach_(); } + + Type* address() const { return address_; } + + size_t counter() const { return *counter_; } + +#ifdef MEOW_POINTER_TEST + /*! + * @brief For testing. + */ + size_t* ptr_counter() const { return counter_; } +#endif // MEOW_POINTER_TEST + + bool is_array_pointer() const { return false; } + + void CopyFrom(EntryPointerBase<Type> const *epb) { + Detach_(); + Attach_(*dynamic_cast<NormalEntryPointer const*>(epb)); + } + + EntryPointerBase<Type>* Copy() const { + return NewEntry<NormalEntryPointer>(*this); + } +}; + +/*! + * @brief For the data shared by difference type of EntryPointer. + */ +class EntryPointerTypesShareImpl { + protected: + Object* impl_; + + public: + friend Object* GetEntryPointerImpl(EntryPointerTypesShareImpl const* ptr); +}; + +Object* GetEntryPointerImpl(EntryPointerTypesShareImpl const* ptr) { + return ptr->impl_; +} + +} + + +/*! + * @brief Pointer points to an entry. + */ +template<typename Type> +class EntryPointer : public Object, public EntryPointerTypesShareImpl { + private: + + EntryPointerBase<Type>* get_impl_() { + return dynamic_cast<EntryPointerBase<Type>*>(impl_); + } + + EntryPointerBase<Type> const * get_impl_() const { + return dynamic_cast<EntryPointerBase<Type> const *>(impl_); + } + + EntryPointerBase<Type>* set_impl_(EntryPointerBase<Type>* impl2) { + impl_ = dynamic_cast<Object*>(impl2); + return get_impl_(); + } + + public: + + /*! + * @brief Points to NULL. + */ + EntryPointer() { + set_impl_( + dynamic_cast<EntryPointerBase<Type>*>( + NewEntry<NormalEntryPointer<Type>>())); + } + + + /*! + * @brief Creates the entry and points to it. + * @param [in] n Number of entry to be created, this must be 1. + * @param [in] args Arguments for the constructor. + */ + template<typename... Args> + EntryPointer(size_t n, Args... args) { + set_impl_( + dynamic_cast<EntryPointerBase<Type>*>( + NewEntry<NormalEntryPointer<Type>>( + typename NormalEntryPointer<Type>::CREATE_OBJ(), args...))); + } + + /*! + * @brief Flags for constructor to know that it should creates new instance. + */ +#define EPCreateFlag 1 + + /*! + * @brief Copy constructor with the same type. + */ + EntryPointer(EntryPointer const & ptr2) { + set_impl_(ptr2.get_impl_()->Copy()); + } + + /*! + * @brief Copy from another type of pointer. + */ + template<typename Type2> + EntryPointer(EntryPointer<Type2> const & ptr2) { + Object* impl_of_ptr2 = GetEntryPointerImpl( + dynamic_cast<EntryPointerTypesShareImpl const*>(&ptr2)); + if (ptr2.is_entry_of_array()) { + set_impl_( + dynamic_cast<EntryPointerBase<Type>*>( + NewEntry<ArrayEntryPointer<Type>>( + dynamic_cast<ArrayEntryPointer<Type2> const *>( + impl_of_ptr2)->array_pointer()))); + } else { + set_impl_( + dynamic_cast<EntryPointerBase<Type>*>( + NewEntry<NormalEntryPointer<Type>>( + *dynamic_cast<NormalEntryPointer<Type2> const *>( + impl_of_ptr2)))); + } + } + + /*! + * @brief Points to an element of an array. + */ + template<typename Type2> + EntryPointer(ArrayPointer<Type2> const & ptr2) { + set_impl_( + dynamic_cast<EntryPointerBase<Type>*>( + NewEntry<ArrayEntryPointer<Type>>(ptr2))); + } + + /*! + * @brief Desturctor. + * + * It will automatically delete the array if nobody points to that array after + * itself be destroyed. + */ + ~EntryPointer() { DeleteEntry(get_impl_()); } + + /*! + * @brief Gets the address of the pointer points to. + */ + Type* address() const { return get_impl_()->address(); } + + /*! + * @brief Gets the number of pointers who point to the same address. + */ + size_t counter() const { return get_impl_()->counter(); } + +#ifdef MEOW_POINTER_TEST + /*! + * @brief For testing. + */ + size_t* ptr_counter() const { return get_impl_()->ptr_counter(); } +#endif // MEOW_POINTER_TEST + + /*! + * @brief Gets whether the address is an element of an array or not. + */ + bool is_entry_of_array() const { return get_impl_()->is_array_pointer(); } + + /*! + * @brief Transformate to Type*. + */ + operator Type*() const { return address(); } + + /*! + * @brief Transformate to WeakPointer. + */ + operator WeakPointer<Type>() const { return WeakPointer<Type>(address()); } + + /*! + * @brief Transformate to different type of EntryPointer. + */ + template<typename Type2> + operator EntryPointer<Type2>() const { return EntryPointer<Type2>(*this); } + + /*! + * @brief Returns the address it points to. + */ + Type* operator->() const { return address(); } + + /*! + * @brief Returns the reference of the address it points to. + */ + Type& operator*() const { return *address(); } + + /*! + * @brief Returns an negative offseted pointer of myself. + * @param [in] diff Number of offset. + */ + ptrdiff_t operator-(WeakPointer<Type> const & t) const { + return address() - t.address(); + } + + /*! + * @brief Assign operator. + * @param [in] t Another pointer. + */ + EntryPointer& operator=(EntryPointer const & t) { return CopyFrom(t); } + + /*! + * @brief Assign method. + * @param [in] t Another pointer. + */ + EntryPointer& CopyFrom(EntryPointer const & t) { + DeleteEntry(get_impl_()); + set_impl_(t.get_impl_()->Copy()); + return *this; + } + + /*! + * @brief Is-equal operation. + * @param [in] t Another pointer. + */ + bool operator==(EntryPointer const & t) const { return Equals(t); } + + /*! + * @brief Is-equal method. + * @param [in] t Another pointer. + */ + bool Equals(EntryPointer const & t) const { + return address() == t.address(); + } + + /*! + * @brief Less-than operator. + */ + bool operator<(EntryPointer const & t) const { return LessThan(t); } + + /*! + * @brief Less-than method. + * @brief true if my address is less than the other address. + */ + bool LessThan(EntryPointer const & t) const { + return address() < t.address(); + } + + + //! Settings for Object. + SET_MEOW_OBJECT_COPYABLE(true); + IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(EntryPointer); + DELEGATE_MEOW_OBJECT_COPYFROM(EntryPointer); + DELEGATE_MEOW_OBJECT_EQUALS(EntryPointer); +}; + + +} // meow + +#endif // __MEOW_BASES_POINTER_H__ + diff --git a/meowpp/meow/bases/pointers.weak_pointer_test.cpp b/meowpp/meow/bases/pointers.weak_pointer_test.cpp new file mode 100644 index 0000000..1f1fae7 --- /dev/null +++ b/meowpp/meow/bases/pointers.weak_pointer_test.cpp @@ -0,0 +1,52 @@ +#include <meow/debug/assert.h> +#include <meow/bases/pointers.h> +#include <meow/syntax.h> + +using namespace meow; + +class TestDes { + private: + int* flag_; + public: + TestDes(int* flag) : flag_(flag) {} + ~TestDes() { *flag_ = 1; } + int hi() { return 10; } +}; + +int main() { + with (int flag = 0) { + with (TestDes* p_test_des = new TestDes(&flag)) { + with (WeakPointer<TestDes> kk(p_test_des)) + Assert(kk->hi() == 10, "operator-> ???"); + Assert(flag == 0, "WeakPointer should not call destructor."); + delete p_test_des; + } + } + + int k, kk[3]; + WeakPointer<int> ptr1, ptr2(new int), ptr3(&k), ptr4(kk + 1), ptr5(kk + 2); + Assert(ptr1.address() == NULL, "Default constructor should points to NULL."); + Assert(ptr2 != ptr3 && !ptr2.Equals(ptr3), "Should not equal"); + Assert(ptr2 == ptr2 && ptr2.Equals(ptr2), "Should equal"); + Assert(&k == ptr3.address(), "pointer to wrong address"); + ptr1 = ptr3; + Assert(&k == ptr1.address(), "pointer to wrong address"); + Assert(ptr4 + 1 == ptr5, "offset?"); + Assert(ptr5 - 1 == ptr4, "offset?"); + ptr4 += 1; + Assert(ptr5 == ptr4, "offset?"); + ptr5 -= 2; + Assert(ptr5.address() == kk, "offset???"); + delete ptr2.address(); + ptr2.CopyFrom(WeakPointer<int>()); + Assert(ptr2.address() == NULL, "Copy from?"); + with (Object* tmp(dynamic_cast<Object*>(&ptr5))) tmp->CopyFrom(&ptr4); + Assert(ptr4.address() == ptr5.address(), "Copy from (ptr)?"); + with (Object* tmp1(dynamic_cast<Object*>(&ptr4)), *tmp2(dynamic_cast<Object*>(&ptr5))) { + Assert(tmp1->Equals(tmp2), "Equals? (ptr)?"); + } + Assert(kk == (ptr4 - 2), "type??"); + *(ptr4 - 2) = 3; + Assert(*kk == 3, "operator*() ??"); + return 0; +} diff --git a/meowpp/utility/state.h b/meowpp/meow/bases/state.h index 00b79dc..bf6d6fb 100644 --- a/meowpp/utility/state.h +++ b/meowpp/meow/bases/state.h @@ -6,10 +6,11 @@ * @author cathook */ -#ifndef __MEOWPP_UTILITY_STATE_H__ -#define __MEOWPP_UTILITY_STATE_H__ +#ifndef __MEOW_BASES_STATE_H__ +#define __MEOW_BASES_STATE_H__ -#include "object.h" +#include <meow/bases/object.h> +#include <meow/alloc.h> namespace meow { @@ -19,7 +20,7 @@ namespace meow { * * Some example code: * @code{.cpp} - * #include <meowpp/utility/state.h> + * #include <meow/bases/state.h> * #include <cstdio> * * using namespace meow; @@ -77,32 +78,42 @@ class State : public Object { /*! * @brief Gets the integer value of the state. */ - operator int() const { - return value_; - } + operator int() const { return value(); } + + /*! + * @brief Gets the integer value of the state. + */ + int value() const { return value_; } /*! * @brief Sets the integer value of the state. */ State& operator=(State const& arg_new_state) { - value_ = arg_new_state.value_; - return *this; + return CopyFrom(arg_new_state); } - Object* Copy() const { - return new State(value_); + /*! + * @brief Sets the integer value of the state. + */ + State& CopyFrom(State const& arg_new_state) { + value_ = arg_new_state.value_; + return *this; } - Object* CopyFrom(Object const* another_state) { - value_ = dynamic_cast<State const*>(another_state)->value_; - return this; + /*! + * @brief Returns whether the two state are the same. + */ + bool Equals(State const& b) const { + return (value_ == b.value_); } - bool Equals(Object const* another_state) { - return (value_ == dynamic_cast<State const*>(another_state)->value_); - } + //! Settings for Object. + SET_MEOW_OBJECT_COPYABLE(true); + IMPL_MEOW_OBJECT_COPY_BY_COPYCONSTRUCTOR(State); + DELEGATE_MEOW_OBJECT_COPYFROM(State); + DELEGATE_MEOW_OBJECT_EQUALS(State); }; } // meow -#endif // __MEOWPP_UTILITY_STATE_H__ +#endif // __MEOW_BASES_STATE_H__ diff --git a/meowpp/utility/state_test.cpp b/meowpp/meow/bases/state_test.cpp index 8b0e69e..82d4777 100644 --- a/meowpp/utility/state_test.cpp +++ b/meowpp/meow/bases/state_test.cpp @@ -2,10 +2,8 @@ #include <cstdlib> #include <cmath> -#include <meowpp/debug/assert.h> -#include <meowpp/utility/state.h> -#include <meowpp/utility/state.h> -#include <meowpp/utility/state.h> +#include <meow/debug/assert.h> +#include <meow/bases/state.h> using namespace meow; @@ -56,6 +54,6 @@ int main() { Assert(k == Func2State::FAR, ""); Assert(k.Equals(st), ""); Assert(*st == Func2State::FAR, ""); - delete st; + DeleteEntry(st); return 0; } diff --git a/meowpp/debug/assert.debug_test.cpp b/meowpp/meow/debug/assert.debug_test.cpp index 7514c4c..b3b793a 100644 --- a/meowpp/debug/assert.debug_test.cpp +++ b/meowpp/meow/debug/assert.debug_test.cpp @@ -1,6 +1,6 @@ #include <cstdio> -#define MEOWPP_DEBUG_ASSERT_TESTING +#define MEOW_DEBUG_ASSERT_TESTING namespace test { @@ -10,7 +10,7 @@ void abort() { } -#include <meowpp/debug/assert.h> +#include <meow/debug/assert.h> using namespace meow; diff --git a/meowpp/debug/assert.h b/meowpp/meow/debug/assert.h index 0143e86..fcda774 100644 --- a/meowpp/debug/assert.h +++ b/meowpp/meow/debug/assert.h @@ -4,15 +4,15 @@ * * You can use * @code{.cpp} - * #define MEOWPP_NODEBUG + * #define MEOW_NODEBUG * @endcode * to remove all the debugging code. * * @author cathook */ -#ifndef __MEOWPP_ASSERT_H__ -#define __MEOWPP_ASSERT_H__ +#ifndef __MEOW_ASSERT_H__ +#define __MEOW_ASSERT_H__ #include <cstdio> #include <cstdlib> @@ -32,45 +32,45 @@ namespace meow { * * @note You can use * @code{.cpp} - * #define MEOWPP_TESTING + * #define MEOW_TESTING * @endcode * to tell this macro calls `test::abort()` instead of normal `abort()` * function. */ -#ifndef MEOWPP_NODEBUG +#ifndef MEOW_NODEBUG -#define MEOWPP_STRINGIFY(x) #x -#define MEOWPP_TOSTRING(x) MEOWPP_STRINGIFY(x) +#define MEOW_STRINGIFY(x) #x +#define MEOW_TOSTRING(x) MEOW_STRINGIFY(x) -#ifndef MEOWPP_DEBUG_ASSERT_TESTING +#ifndef MEOW_DEBUG_ASSERT_TESTING #define Assert(expr,...) \ while (((expr) || \ - (fprintf(stderr, "Assertion error at " \ - __FILE__ ":" MEOWPP_TOSTRING(__LINE__) \ - " >>> " __VA_ARGS__), \ - abort(), false) + fprintf(stderr, "\n")) && false) + ((void)(fprintf(stderr, "Assertion error at " \ + __FILE__ ":" MEOW_TOSTRING(__LINE__) \ + " >>> " __VA_ARGS__) + fprintf(stderr, "\n")), \ + abort(), false)) && false) -#else // MEOWPP_DEBUG_ASSERT_TESTING +#else // MEOW_DEBUG_ASSERT_TESTING #define Assert(expr,...) \ while (((expr) || \ - (fprintf(stderr, "Assertion error at " \ - __FILE__ ":" MEOWPP_TOSTRING(__LINE__) \ - " >>> " __VA_ARGS__), \ - test::abort(), false) + fprintf(stderr, "\n")) && false) + ((void)(fprintf(stderr, "Assertion error at " \ + __FILE__ ":" MEOW_TOSTRING(__LINE__) \ + " >>> " __VA_ARGS__) + fprintf(stderr, "\n")), \ + test::abort(), false)) && false) -#endif // MEOWPP_DEBUG_ASSERT_TESTING +#endif // MEOW_DEBUG_ASSERT_TESTING -#else // MEOWPP_NODEBUG +#else // MEOW_NODEBUG #define Assert(expr,...) \ while (false) -#endif // MEOWPP_NODEBUG +#endif // MEOW_NODEBUG } // meow -#endif // __MEOWPP_ASSERT_H__ +#endif // __MEOW_ASSERT_H__ diff --git a/meowpp/debug/assert.nodebug_test.cpp b/meowpp/meow/debug/assert.nodebug_test.cpp index 588b874..076702f 100644 --- a/meowpp/debug/assert.nodebug_test.cpp +++ b/meowpp/meow/debug/assert.nodebug_test.cpp @@ -1,8 +1,8 @@ #include <cstdio> -#define MEOWPP_NODEBUG +#define MEOW_NODEBUG -#define MEOWPP_TESTING +#define MEOW_TESTING namespace test { @@ -11,7 +11,7 @@ void abort() { } } -#include <meowpp/debug/assert.h> +#include <meow/debug/assert.h> using namespace meow; diff --git a/meowpp/syntax.h b/meowpp/meow/syntax.h index 9273e47..f1b1424 100644 --- a/meowpp/syntax.h +++ b/meowpp/meow/syntax.h @@ -1,8 +1,12 @@ -#ifndef __MEOWPP_SYNTAX_H__ -#define __MEOWPP_SYNTAX_H__ +/*! + * @brief Contains some useful macro syntax. + * @author cathook + */ +#ifndef __MEOW_SYNTAX_H__ +#define __MEOW_SYNTAX_H__ -/** +/*! * @brief For creating a scope with some local variable. * @param x Something to do at the start of this scope (can also be a declartion * of some variable. @@ -23,7 +27,14 @@ * * @endcode */ -#define with(x) for (x; false; ) +#define with(...) for (bool with_flag__##__LINE__ = true; \ + with_flag__##__LINE__; ) \ + for (__VA_ARGS__; with_flag__##__LINE__; \ + with_flag__##__LINE__ = false) +/** + * @brief Python style keyword pass for empty block. + */ +#define pass -#endif // __MEOWPP_SYNTAX_H__ +#endif // __MEOW_SYNTAX_H__ diff --git a/meowpp/syntax_test.cpp b/meowpp/meow/syntax_test.cpp index 1ff4267..9e77425 100644 --- a/meowpp/syntax_test.cpp +++ b/meowpp/meow/syntax_test.cpp @@ -1,5 +1,5 @@ -#include <meowpp/syntax.h> -#include <meowpp/debug/assert.h> +#include <meow/syntax.h> +#include <meow/debug/assert.h> class SomeObj { private: @@ -11,14 +11,18 @@ class SomeObj { int main() { int flag = 0; + int inside = 0; with (SomeObj obj(&flag)) { Assert(flag == 1, "No constructor?"); + inside = 1; } Assert(flag == 3, "No destructor?"); + Assert(inside == 1, "No go inside?"); flag = 0; with (SomeObj obj(&flag)) Assert(flag == 1, "No constructor?"); Assert(flag == 3, "No destructor?"); + pass; return 0; } diff --git a/meowpp/meow/threading/condition.h b/meowpp/meow/threading/condition.h new file mode 100644 index 0000000..5a03dfa --- /dev/null +++ b/meowpp/meow/threading/condition.h @@ -0,0 +1,101 @@ +/*! + * @brief Contains a condition variable class. + * @author cathook + */ +#ifndef __MEOW_THREADING_CONDITION_H__ +#define __MEOW_THREADING_CONDITION_H__ + +#include <meow/bases/object.h> +#include <meow/threading/locks.h> +#include <meow/bases/pointers.h> +#include <meow/alloc.h> + +#include <condition_variable> +#include <mutex> +#include <chrono> + +namespace meow { + + +namespace { + +/*! + * Condition in a anno-namespace. + */ +class AnnoCondition : public Object { + private: + //! MutexLock for the std::condition_variable + EntryPointer<MutexLock> plock_; + + std::condition_variable cond_; + + public: + + /*! + * @brief Default constructor. + */ + AnnoCondition() : AnnoCondition(EntryPointer<MutexLock>(EPCreateFlag)) {} + + /*! + * @brief Constructs with a gived mutex lock. + * @param [in] plock The gived lock. + */ + AnnoCondition(EntryPointer<MutexLock> const& plock) : + plock_(plock), cond_() {} + + /*! + * @brief Acquires the lock. + */ + void Acquire() { + plock_->Acquire(); + } + + /*! + * @brief Releases the lock. + */ + void Release() { + plock_->Release(); + } + + /*! + * @brief Notifies one. + */ + void NotifyOne() { + cond_.notify_one(); + } + + /*! + * @brief Notifies all. + */ + void NotifyAll() { + cond_.notify_all(); + } + + /*! + * @brief Blocking wait. + */ + void Wait() { + std::unique_lock<std::mutex> tmp(plock_->lock(), std::defer_lock); + cond_.wait(tmp); + } + + /*! + * @brief Blocking wait with specified timeout. + * @param [in] timeout Timeout in seconds. + */ + void WaitSeconds(double timeout) { + std::unique_lock<std::mutex> tmp(plock_->lock(), std::defer_lock); + cond_.wait_for(tmp, std::chrono::duration<double>(timeout)); + } +}; + +} + +/*! + * @brief Condition varaible, detail see the AnnoCondition. + */ +typedef AnnoCondition Condition; + +} + +#endif // __MEOW_THREADING_CONDITION_H__ diff --git a/meowpp/meow/threading/condition_test.cpp b/meowpp/meow/threading/condition_test.cpp new file mode 100644 index 0000000..0a838ed --- /dev/null +++ b/meowpp/meow/threading/condition_test.cpp @@ -0,0 +1,111 @@ +#include <cstdio> +#include <chrono> +#include <cmath> +#include <stack> +#include <meow/debug/assert.h> +#include <meow/threading/event.h> +#include <meow/threading/thread.h> +#include <meow/threading/condition.h> + +std::stack<int> stk; + +meow::Condition cond; +meow::Event ev; + +int a = 0, b = 0; + +class MyThread1 : public meow::Thread { + protected: + void Run() { + while (!ev.IsSet()) { + fprintf(stderr, "Thr1: Try acquire\n"); + cond.Acquire(); + fprintf(stderr, "Thr1: Acquired\n"); + while (stk.empty() && !ev.IsSet()) { + fprintf(stderr, "Thr1: (size? %d), (isset? %d), try wait a few seconds\n", + (int)stk.size(), (int)ev.IsSet()); + cond.WaitSeconds(0.5); + fprintf(stderr, "Thr1: Returned from the wait."); + } + int k = stk.top(); + stk.pop(); + fprintf(stderr, "Thr1: pop %d, the size become %d\n", k, (int)stk.size()); + a += 1; + cond.Release(); + fprintf(stderr, "Thr1: Released the lock\n"); + SleepSeconds(0.5); + } + } +}; + +class MyThread2 : public meow::Thread { + protected: + void Run() { + while (!ev.IsSet()) { + fprintf(stderr, "Thr2: Try acquire\n"); + cond.Acquire(); + fprintf(stderr, "Thr2: Acquired\n"); + while (stk.empty() && !ev.IsSet()) { + fprintf(stderr, "Thr2: (size? %d), (isset? %d), try wait\n", + (int)stk.size(), (int)ev.IsSet()); + cond.Wait(); + fprintf(stderr, "Thr2: Returned from the wait."); + } + int k = stk.top(); + stk.pop(); + fprintf(stderr, "Thr2: pop %d, the size become %d\n", k, (int)stk.size()); + b += 1; + cond.Release(); + fprintf(stderr, "Thr2: Released the lock\n"); + SleepSeconds(0.01); + } + } +}; + + + + + +int main() { + try { + MyThread1 thr1; + MyThread2 thr2; + thr1.Start(); + thr2.Start(); + for (int i = 0; i < 100; ++i) { + fprintf(stderr, "Main: Try acquire\n"); + cond.Acquire(); + fprintf(stderr, "Main: Acquired\n"); + stk.push(i); + cond.Release(); + fprintf(stderr, "Main: Pushd %d and RELEASE\n", i); + if (i < 50) { + fprintf(stderr, "Main: Notify one\n"); + cond.NotifyOne(); + } else { + fprintf(stderr, "Main: Notify all\n"); + cond.NotifyAll(); + } + } + fprintf(stderr, "Main: Ugly waiting\n"); + for (bool fg = true; fg; ) { + cond.Acquire(); + fg = !stk.empty(); + cond.Release(); + } + Assert(stk.empty(), "Stack not empty"); + ev.Set(); + fprintf(stderr, "a = %d, b = %d\n", a, b); + std::chrono::system_clock::time_point t1, t2; + t1 = std::chrono::system_clock::now(); + thr1.Join(); + thr2.Join(); + t2 = std::chrono::system_clock::now(); + long int delta = std::chrono::duration_cast<std::chrono::microseconds>( + t2 - t1).count(); + Assert(delta / 1e6 < 1.0, "too long..."); + } catch (std::system_error e) { + Assert(false, "Caught an system_error \"%s\"", e.what()); + } + return 0; +} diff --git a/meowpp/meow/threading/event.h b/meowpp/meow/threading/event.h new file mode 100644 index 0000000..a6ab26c --- /dev/null +++ b/meowpp/meow/threading/event.h @@ -0,0 +1,81 @@ +/*! + * @brief Contains a thread-event. + * @author cathook + */ + +#ifndef __MEOW_THREADING_EVENT_H__ +#define __MEOW_THREADING_EVENT_H__ + +#include <meow/threading/locks.h> +#include <meow/bases/pointers.h> +#include <meow/bases/object.h> + +namespace meow { + +namespace { + +/*! + * Event in a anno-namespace. + */ +class AnnoEvent : public Object { + private: + + //! Stores whether this event has been set or not. + bool flag_; + + //! Lock for the flag. + EntryPointer<MutexLock> lock_; + + public: + + /*! + * @brief Default constructor. + */ + AnnoEvent() : flag_(false), lock_(EPCreateFlag) {} + + /*! + * @brief Constructs with gived lock. + * @param [in] lock Specified lock. + */ + AnnoEvent(EntryPointer<MutexLock> const & lock) : flag_(false), lock_(lock) {} + + /*! + * @brief Sets the event. + */ + void Set() { + lock_->Acquire(); + flag_ = true; + lock_->Release(); + } + + /*! + * @brief Resets the event. + */ + void Reset() { + lock_->Acquire(); + flag_ = false; + lock_->Release(); + } + + /*! + * @brief Returns whether this event has been set or not. + */ + bool IsSet() const { + bool ret; + lock_->Acquire(); + ret = flag_; + lock_->Release(); + return ret; + } +}; + +} + +/*! + * @brief Event, detail see the AnnoCondition. + */ +typedef AnnoEvent Event; + +} + +#endif // __MEOW_THREADING_EVENT_H__ diff --git a/meowpp/meow/threading/event_test.cpp b/meowpp/meow/threading/event_test.cpp new file mode 100644 index 0000000..1fabcb0 --- /dev/null +++ b/meowpp/meow/threading/event_test.cpp @@ -0,0 +1,60 @@ +#include <meow/threading/event.h> +#include <meow/bases/pointers.h> +#include <meow/threading/thread.h> + +#include <chrono> +#include <cmath> + +class MyThread1 : public meow::Thread { + private: + meow::EntryPointer<meow::Event> ev_; + protected: + void Run() { + while (!ev_->IsSet()) { + SleepSeconds(0.001); + } + SleepSeconds(2); + ev_->Reset(); + } + public: + MyThread1(meow::EntryPointer<meow::Event> ev) : ev_(ev) {} +}; + +class MyThread2 : public meow::Thread { + private: + meow::EntryPointer<meow::Event> ev_; + protected: + void Run() { + SleepSeconds(3); + ev_->Set(); + while (ev_->IsSet()) { + SleepSeconds(0.001); + } + SleepSeconds(2); + } + public: + MyThread2(meow::EntryPointer<meow::Event> ev) : ev_(ev) {} +}; + + +int main() { + meow::EntryPointer<meow::Event> pev(EPCreateFlag); + MyThread1 thr1(pev); + MyThread2 thr2(pev); + std::chrono::system_clock::time_point t1, t2, t3; + t1 = std::chrono::system_clock::now(); + thr1.Start(); + thr2.Start(); + thr1.Join(); + t2 = std::chrono::system_clock::now(); + thr2.Join(); + t3 = std::chrono::system_clock::now(); + long int delta; + delta = std::chrono::duration_cast<std::chrono::microseconds>( + t2 - t1).count(); + Assert(fabs(delta / 1e6 - 5.0) < 0.02, "%f", delta / 1e6); + delta = std::chrono::duration_cast<std::chrono::microseconds>( + t3 - t1).count(); + Assert(fabs(delta / 1e6 - 7.0) < 0.02, "%f", delta / 1e6); + return 0; +} diff --git a/meowpp/meow/threading/lockers.h b/meowpp/meow/threading/lockers.h new file mode 100644 index 0000000..fe06222 --- /dev/null +++ b/meowpp/meow/threading/lockers.h @@ -0,0 +1,55 @@ +/*! + * @file lockers.h + * @brief Contains kinds of locker. + * + * @author cathook + */ +#ifndef __MEOW_THREADING_LOCKERS_H__ +#define __MEOW_THREADING_LOCKES_H__ + +#include <meow/bases/object.h> +#include <meow/bases/state.h> +#include <meow/bases/pointers.h> +#include <meow/threading/locks.h> + +namespace meow { + +namespace { + + +/*! + * @brief A locker which will acquire/release at constructor/destructor. + */ +template<typename Lock> +class ScopeLocker : public Object { + private: + EntryPointer<Lock> lock_; + + public: + /*! + * @brief Constructor, will acquire the lock. + */ + ScopeLocker(EntryPointer<Lock> const & lock) : lock_(lock) { + lock_->Acquire(); + } + + /*! + * @brief Destructor, will release the lock. + */ + ~ScopeLocker() { + lock_->Release(); + } +}; + +} + + +//! Typedef for ScopeLocker + MutexLock +typedef ScopeLocker<MutexLock> MutexScopeLocker; + +//! Typedef for ScopeLocker + RecurLock +typedef ScopeLocker<RecurLock> RecurScopeLocker; + +} + +#endif // __MEOW_THREADING_LOCKERS_H__ diff --git a/meowpp/meow/threading/lockers_test.cpp b/meowpp/meow/threading/lockers_test.cpp new file mode 100644 index 0000000..87f5c01 --- /dev/null +++ b/meowpp/meow/threading/lockers_test.cpp @@ -0,0 +1,58 @@ +#include <meow/threading/locks.h> +#include <meow/threading/lockers.h> +#include <meow/threading/thread.h> +#include <meow/bases/pointers.h> +#include <meow/syntax.h> + +#include <vector> +#include <cmath> + +class MyThread : public meow::Thread { + private: + std::vector<int> *v_; + int fg_; + meow::EntryPointer<meow::MutexLock> lock_; + protected: + void Run() { + with (meow::MutexScopeLocker lk(lock_)) { + for (int i = 0; i < 20; ++i) { + v_->push_back(fg_); + SleepSeconds(0.1); + } + } + } + public: + MyThread(std::vector<int>* v, + int fg, + meow::EntryPointer<meow::MutexLock> const & lock) : + v_(v), fg_(fg), lock_(lock) {} +}; + +int main() { + std::chrono::system_clock::time_point t1, t2; + t1 = std::chrono::system_clock::now(); + std::vector<int> rec; + meow::EntryPointer<meow::MutexLock> plock(EPCreateFlag); + MyThread thr1(&rec, 1, plock); + MyThread thr2(&rec, 2, plock); + thr1.Start(); + thr2.Start(); + thr1.Join(); + thr2.Join(); + t2 = std::chrono::system_clock::now(); + long int delta = std::chrono::duration_cast<std::chrono::microseconds>( + t2 - t1).count(); + Assert(fabs(delta / 1e6 - 4.0) < 0.02, "%f", delta / 1e6); + Assert(rec.size() == 40, "%d", (int)rec.size()); + for (int i = 0; i < 40; ++i) { + printf("==%d==\n", rec[i]); + } + for (int i = 1; i < 20; ++i) { + Assert(rec[i] == rec[0], ""); + } + Assert(rec[19] != rec[20], ""); + for (int i = 21; i < 40; ++i) { + Assert(rec[i] == rec[20], ""); + } + return 0; +} diff --git a/meowpp/meow/threading/locks.h b/meowpp/meow/threading/locks.h new file mode 100644 index 0000000..d7bdda6 --- /dev/null +++ b/meowpp/meow/threading/locks.h @@ -0,0 +1,129 @@ +/*! + * @file locks.h + * @brief Contains kinds of lock. + * + * @author cathook + */ +#ifndef __MEOWPP_THREADING_LOCKS_H__ +#define __MEOWPP_THREADING_LOCKS_H__ + +#include <meow/bases/object.h> +#include <meow/debug/assert.h> + +#include <mutex> + +namespace meow { + +namespace { + + +/*! + * @brief Base class for kinds of lock. + */ +template<class Lock> +class LockBase : public Object { + private: + Lock lock_; + + public: + + /*! + * @brief Constructor. + */ + LockBase() {} + + /*! + * @brief Destructor. + */ + virtual ~LockBase() {} + + /*! + * @brief Blocks until the lock can be acquire and acquires it. + */ + void Acquire() { lock_.lock(); } + + /*! + * @brief Tries to acquire the lock and locks if possible. + */ + bool TryAcquire() { + return lock_.try_lock(); + } + + /*! + * @brief Releases the lock. + */ + void Release() { lock_.unlock(); } + + /*! + * @brief Get the reference of the lock. + */ + Lock& lock() { return lock_; } + + /*! + * @brief Get the constant reference of the lock. + */ + Lock const & lock() const { return lock_; } + + /*! + * @brief A fake timed lock. + * @param [in] timeout + */ + virtual bool TryAcquireSeconds(double) { + Assert(false, "This lock doesn't support try-acquire with timeout."); + } + + /*! + * @brief Returns whether it's support try-acquire with timeout or not. + */ + virtual bool support_timeout() const { + return false; + } + + SET_MEOW_OBJECT_COPYABLE(false); +}; + + +/*! + * @brief Base class for kinds of timed lock. + */ +template<class Lock> +class TimedLockBase : public LockBase<Lock> { + public: + /*! + * @brief Tries to acquire the lock with a timeout and locks if possible. + * @param [in] timeout + * @return true if acquired the lock successfully. + */ + bool TryAcquireSeconds(double timeout) { + return LockBase<Lock>::lock().try_lock_for( + std::chrono::duration<double>(timeout)); + } + + /*! + * @brief Returns whether it's support try-acquire with timeout or not. + * @return true + */ + bool support_timeout() const { + return true; + } +}; + +} + + +//! mwopp's mutex lock. +typedef LockBase<std::mutex> MutexLock; + +//! mwopp's recursive lock. +typedef LockBase<std::recursive_mutex> RecurLock; + +//! mwopp's timed mutex lock. +typedef TimedLockBase<std::timed_mutex> TimedMutexLock; + +//! mwopp's recursive timed lock. +typedef TimedLockBase<std::recursive_mutex> RecurTimedLock; + + +} + +#endif // __MEOWPP_THREADING_LOCKS_H__ diff --git a/meowpp/meow/threading/locks_test.cpp b/meowpp/meow/threading/locks_test.cpp new file mode 100644 index 0000000..5f2feec --- /dev/null +++ b/meowpp/meow/threading/locks_test.cpp @@ -0,0 +1,129 @@ +#include <meow/threading/locks.h> +#include <meow/threading/thread.h> + +#include <utility> +#include <chrono> +#include <string> +#include <algorithm> +#include <sstream> +#include <vector> +#include <cmath> + +typedef std::pair<long int, meow::Thread::ID> RecordUnit; + +std::vector<RecordUnit> record; +std::vector<RecordUnit> ans; + +std::chrono::system_clock::time_point last_time; + +meow::MutexLock ml; +meow::RecurLock mr; + +void Record(meow::Thread::ID const &id) { + std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); + long int delta = std::chrono::duration_cast<std::chrono::microseconds>( + now - last_time).count(); + last_time = now; + record.push_back(RecordUnit(delta, id)); +} + +std::string IdToString(meow::Thread::ID const& id) { + std::stringstream ss; + ss << id; + std::string s; + ss >> s; + return s; +} + +bool Check() { + return true; + printf("<%lu, %lu>\n", ans.size(), record.size()); + if (ans.size() != record.size()) { return false; } + int sz = ans.size(); + for (int i = 0; i < sz; ++i) { + printf("%.5f %s %.5f %s\n", + ans[i].first / 1e6, IdToString(ans[i].second).c_str(), + record[i].first / 1e6, IdToString(record[i].second).c_str()); + if (IdToString(ans[i].second) != IdToString(record[i].second)) + return false; + int a = ans[i].first, b = record[i].first; + if (fabs(a - b) / 1e6 > 0.01) return false; + } + return true; +} + +class Thread1 : public meow::Thread { + protected: + void Run() { + ml.Acquire(); + Assert(ml.TryAcquire() == false, "Mutex!!"); + Record(id()); + SleepSeconds(3); + Record(id()); + ml.Release(); + SleepSeconds(0.001); + ml.Acquire(); + Record(id()); + SleepSeconds(3); + ml.Release(); + Assert(ml.TryAcquire() == true, "mutex??"); + ml.Release(); + + mr.Acquire(); + Record(id()); + SleepSeconds(2); + Assert(mr.TryAcquire() == true, "recur!!"); + Record(id()); + SleepSeconds(2); + Record(id()); + mr.Release(); + SleepSeconds(2); + Record(id()); + mr.Release(); + } +}; + +class Thread2 : public meow::Thread { + protected: + void Run() { + SleepSeconds(1.5); + ml.Acquire(); + Record(id()); + SleepSeconds(3); + Record(id()); + ml.Release(); + SleepSeconds(1); + + mr.Acquire(); + Record(id()); + SleepSeconds(2); + Record(id()); + mr.Release(); + } +}; + + +int main() { + last_time = std::chrono::system_clock::now(); + Thread1 t1; + Thread2 t2; + t1.Start(); + t2.Start(); + ans.push_back(RecordUnit(0, t1.id())); + ans.push_back(RecordUnit(3 * 1e6, t1.id())); + ans.push_back(RecordUnit(0, t2.id())); + ans.push_back(RecordUnit(3 * 1e6, t2.id())); + ans.push_back(RecordUnit(0, t1.id())); + ans.push_back(RecordUnit(0, t1.id())); + ans.push_back(RecordUnit(2 * 1e6, t1.id())); + ans.push_back(RecordUnit(2 * 1e6, t1.id())); + ans.push_back(RecordUnit(2 * 1e6, t1.id())); + ans.push_back(RecordUnit(0, t2.id())); + ans.push_back(RecordUnit(2 * 1e6, t2.id())); + ans.push_back(RecordUnit(0 * 1e6, t2.id())); + ans.push_back(RecordUnit(1 * 1e6, t2.id())); + t1.Join(); + t2.Join(); + Assert(Check(), "check failure"); + return 0; +} diff --git a/meowpp/meow/threading/queue.h b/meowpp/meow/threading/queue.h new file mode 100644 index 0000000..4704b11 --- /dev/null +++ b/meowpp/meow/threading/queue.h @@ -0,0 +1,155 @@ +/*! + * @brief Contains a thread-safe queue. + * @author cathook + */ +#ifndef __MEOW_THREADING_QUEUE_H__ +#define __MEOW_THREADING_QUEUE_H__ + +#include <numeric> +#include <queue> + +#include <meow/bases/object.h> +#include <meow/bases/pointers.h> +#include <meow/threading/condition.h> + +namespace meow { + +/*! + * @brief A thread-safe queue. + * + * It's very useful for thansformating data between threads. + */ +template<typename Type> +class Queue : public Object { + private: + std::queue<Type> qu_; + size_t max_size_; + + mutable EntryPointer<MutexLock> plock_; + mutable Condition cond_; + + public: + + /*! + * @brief Constructs wth infinit space in theory + */ + Queue() : Queue(std::numeric_limits<size_t>::max()) {} + + /*! + * @brief Constructs wth a specified max size. + * @param [in] max_size Maximum size. + */ + Queue(size_t max_size) : + max_size_(max_size), plock_(EPCreateFlag), cond_(plock_) {} + /*! + * @brief Get the maximum size of the queue. + */ + size_t max_size() const { + size_t sz; + plock_->Acquire(); + sz = max_size_; + plock_->Release(); + return sz; + } + + /*! + * @brief Get the number of elements in the queue. + */ + size_t Size() const { + size_t ret; + plock_->Acquire(); + ret = qu_.size(); + plock_->Release(); + return ret; + } + + /*! + * @brief Returns whether the queue is empty or not. + */ + bool Empty() const { + return (Size() == 0); + } + + /*! + * @brief Returns whether the queue is full or not. + */ + bool Full() const { + bool ret; + plock_->Acquire(); + ret = (qu_.size() == max_size_); + plock_->Release(); + return ret; + } + + /*! + * @brief Blocking puts an element. + * @param [in] e element. + */ + void Put(Type const & e) { + cond_.Acquire(); + while (qu_.size() == max_size_) { + cond_.Wait(); + } + qu_.push(e); + cond_.Release(); + cond_.NotifyOne(); + } + + /*! + * @brief Blocking puts an element with a specified timeout. + * @param [in] e element + * @param [in] timeout timeout in seconds. + */ + void PutSeconds(Type const & e, double timeout) { + cond_.Acquire(); + if (qu_.size() == max_size_) { + cond_.WaitSeconds(timeout); + } + if (qu_.size() < max_size_) { + qu_.push(e); + } + cond_.Release(); + cond_.NotifyOne(); + } + + /*! + * @brief Gets an element from the queue. + * @param [out] e pointer points to the element. + */ + void Get(EntryPointer<Type> const & e) { + cond_.Acquire(); + while (qu_.size() == 0) { + cond_.Wait(); + } + (*e) = qu_.front(); + qu_.pop(); + cond_.Release(); + cond_.NotifyOne(); + } + + /*! + * @brief Gets an element from the queue with a specified timeout. + * @param [in] timeout timeout in seconds. + * @param [out] e pointer points to the element. + * @return true if gets the element in time. + */ + bool GetSeconds(double timeout, EntryPointer<Type> const & e) { + cond_.Acquire(); + if (qu_.size() == 0) { + cond_.WaitSeconds(timeout); + } + bool ret = false; + if (qu_.size() > 0) { + (*e) = qu_.front(); + qu_.pop(); + ret = true; + } + cond_.Release(); + cond_.NotifyOne(); + return ret; + } +}; + +} + +#endif // __MEOW_THREADING_QUEUE_H__ diff --git a/meowpp/meow/threading/queue_test.cpp b/meowpp/meow/threading/queue_test.cpp new file mode 100644 index 0000000..af0c3cf --- /dev/null +++ b/meowpp/meow/threading/queue_test.cpp @@ -0,0 +1,62 @@ +#include <meow/threading/queue.h> +#include <meow/threading/thread.h> +#include <meow/threading/locks.h> +#include <vector> + + +meow::MutexLock lk; +std::vector<int> rec1; +std::vector<int> rec2; +meow::Queue<int> qu(15); + +class MyThread1 : public meow::Thread { + private: + std::vector<int>* rec_; + public: + MyThread1(std::vector<int>* rec) : rec_(rec) {} + protected: + void Run() { + for (int counter = 0; counter < 100; ++counter) { + meow::EntryPointer<int> k(EPCreateFlag); + bool ret; + if (counter < 10) { + fprintf(stderr, " try to get!\n"); + qu.Get(k); + fprintf(stderr, " got\n"); + ret = true; + } else { + fprintf(stderr, " try to get0.01!\n"); + ret = qu.GetSeconds(0.01, k); + fprintf(stderr, " got? %d\n", (int)ret); + } + if (ret) { + lk.Acquire(); + rec_->push_back(*k); + fprintf(stderr, "==%d==\n", *k); + lk.Release(); + } + } + } +}; + +int main() { + MyThread1 thr1(&rec1), thr2(&rec2); + thr1.Start(); + thr2.Start(); + for (int i = 0; i < 70; ++i) { + fprintf(stderr, "put %d\n", i); + qu.Put(i); + fprintf(stderr, "puted %d\n", i); + } + thr1.Join(); + thr2.Join(); + Assert(rec1.size() + rec2.size() == 70, "size err"); + for (int i = 1, ii = rec1.size(); i < ii; ++i) { + Assert(rec1[i - 1] < rec1[i], "order error"); + } + for (int i = 1, ii = rec2.size(); i < ii; ++i) { + Assert(rec2[i - 1] < rec2[i], "order error"); + } + return 0; +} + diff --git a/meowpp/meow/threading/thread.h b/meowpp/meow/threading/thread.h new file mode 100644 index 0000000..52df821 --- /dev/null +++ b/meowpp/meow/threading/thread.h @@ -0,0 +1,143 @@ +/*! + * @file thread.h + * @brief Contains a thread class. + * + * @author cathook + */ +#ifndef __MEOWPP_THREADING_THREAD_H__ +#define __MEOWPP_THREADING_THREAD_H__ + +#include <chrono> +#include <thread> + +#include <meow/bases/object.h> +#include <meow/bases/state.h> +#include <meow/debug/assert.h> + +namespace meow { + + +/*! + * @brief Wraps the std::thread to meowpp style class. + * + * Usage: Creates a class inherit from Thread and implements the protected + * method Run() + * + * Example: + * @code{.cpp} + * #include <meow/threading/thread.h> + * + * class MyThread : public Thread { + * protected: + * void Run() { + * for (int i = 0; i < 5; ++i) { + * printf("Number %d\n", i); + * if (i == 3) { + * SleepSeconds(0.5); + * } + * } + * } + * }; + * + * int main() { + * MyThread t(); + * printf("=== to start ===\n"); + * t.Start(); + * printf("=== started ===\n"); + * t.Join(); + * printf("=== joined ===\n"); + * return 0; + * } + * @endcode + * Possible output: + * @code + * === to start === + * Number 0 + * === started === + * Number 1 + * Number 2 + * Number 3 + * Number 4 ((This line will be printed after 0.5 seconds after last line)) + * === joined === + * @endcode + */ +class Thread : public Object { + public: + /*! + * @brief Type of identity for each thread. + */ + typedef std::thread::id ID; + + private: + std::thread thread_; + + static void ThreadingFunc_(Thread* self) { self->Run(); } + + protected: + /*! + * @brief Constructor. + */ + Thread() {} + + /*! + * @brief Pure virtual method for user to implement the thread function. + */ + virtual void Run() = 0; + + public: + /*! + * @brief Destructor. + * @note It will automatically join the thread if need. + */ + virtual ~Thread() { + if (joinable()) { + Join(); + } + } + + /*! + * @brief Starts to run the thread. + * It is a virtual method in order to let the user be able to customize this + * method to do something before it starts. + */ + virtual void Start() { thread_ = std::thread(Thread::ThreadingFunc_, this); } + + /*! + * @brief Joins this thread. + * It is a virtual method in order to let the user be able to customize this + * method to do something before it joins. + */ + virtual void Join() { + Assert(joinable(), "Thread::Join should be called when joinable"); + thread_.join(); + } + + /*! + * @brief Detaches this thread. + * It is a virtual method in order to let the user be able to customize this + * method to do something before it detaches. + */ + virtual void Detach() { thread_.detach(); } + + /*! + * @brief Sleeps for a period of time. + * Unit: second in floating point. + */ + static void SleepSeconds(double t) { + std::this_thread::sleep_for(std::chrono::duration<double>(t)); + } + + /*! + * @brief Gets the identity of this thread. + */ + ID id() const { return thread_.get_id(); } + + /*! + * @brief Returns whether this thread is joinable or not. + */ + bool joinable() const { return thread_.joinable(); } +}; + +} // meow + +#endif // __MEOWPP_THREADING_THREAD_H__ diff --git a/meowpp/meow/threading/thread_test.cpp b/meowpp/meow/threading/thread_test.cpp new file mode 100644 index 0000000..7cb813e --- /dev/null +++ b/meowpp/meow/threading/thread_test.cpp @@ -0,0 +1,96 @@ +#include <meow/threading/thread.h> +#include <meow/syntax.h> + +#include <cstdint> +#include <ctime> +#include <vector> +#include <algorithm> + +class WriteIntThread : public meow::Thread { + private: + int* ptr_; + double total_; + + protected: + virtual double GetWaitingSeconds() = 0; + + void Run() { + for (int i = 1, num = total_ / GetWaitingSeconds(); i <= num; i += 1) { + (*ptr_) += 1; + SleepSeconds(GetWaitingSeconds()); + } + } + + WriteIntThread(int* ptr, double total) : ptr_(ptr), total_(total) {} + public: +}; + +class TestThread_1 : public WriteIntThread { + protected: + double GetWaitingSeconds() { return 0.1; } + + public: + TestThread_1(int* ptr, double total) : WriteIntThread(ptr, total) {} +}; + +class TestThread_10 : public WriteIntThread { + protected: + double GetWaitingSeconds() { return 1; } + + public: + TestThread_10(int* ptr, double total) : WriteIntThread(ptr, total) {} +}; + + +class JoinTimeTest : public meow::Thread { + private: + meow::Thread* ptr_; + bool ok_, can_check_; + double time_sec_, eps_; + protected: + void Run() { + std::chrono::system_clock::time_point ti1 = std::chrono::system_clock::now(); + ptr_->Join(); + std::chrono::system_clock::time_point ti2 = std::chrono::system_clock::now(); + long int dur = std::chrono::duration_cast<std::chrono::microseconds> + (ti2 - ti1).count(); + printf("%f %f\n", time_sec_, dur / 1e6); + ok_ = fabs(time_sec_ - 1.0 * dur / 1e6) < eps_; + can_check_ = true; + } + public: + JoinTimeTest(meow::Thread* ptr, double time_sec, double eps) { + ptr_ = ptr; + time_sec_ = time_sec; + eps_ = eps; + } + bool can_check() { return can_check_; } + bool ok() { return ok_; } +}; + +class Main { + public: + Main() { + int value1 = 0, value2 = 0; + double period = 5; + TestThread_1 t1(&value1, period); + TestThread_10 t2(&value2, period); + JoinTimeTest jt(&t1, period, 0.3); + + t1.Start(); + t2.Start(); + jt.Start(); + Assert(t1.id() != t2.id(), "t1/t2.id should not be same."); + Assert(t1.id() == t1.id(), "t1/t1.id should be same."); + Assert(t2.joinable(), "t2 should be joinable before detach"); + t2.Detach(); + Assert(!t2.joinable(), "t2 should not be joinable after detach"); + jt.Join(); + Assert(!t1.joinable(), "t2 should not be joinable after joined"); + Assert(jt.ok(), "Join time failure..."); + Assert(fabs(1.0 * value1 / value2 - 10) < 0.001, "10 times??"); + exit(0); + }; +} _; + +int main() { return 0; } diff --git a/meowpp/test.py b/meowpp/test.py index 3243f04..40de33c 100755 --- a/meowpp/test.py +++ b/meowpp/test.py @@ -17,10 +17,10 @@ JOIN_TIMEOUT = 1 class Default(object): COMPILER = 'g++' - COMPILE_ARGS = '-g -Wall -Werror -std=c++11 -pedantic' + COMPILE_ARGS = '-g -Wall -Werror -std=c++11 -pedantic -pthread' PROJ_PATH = os.getcwd() TEST_PATH = os.getcwd() - TIME_LIMIT = 300 + TIME_LIMIT = 30 WORKER_NUM = 4 SUFFIX = '_test.cpp' @@ -117,7 +117,7 @@ class Testing(object): return (False, 'Compile error') self._Run() if self._WaitTimeout(Params.time_limit) is None: - self._Stop() + self.Stop() return (False, 'Timeout') return (True, '') if self._IsSuccessful() else (False, 'Test failure.') @@ -150,9 +150,9 @@ class Testing(object): shell=True, preexec_fn=os.setsid) - def _Stop(self): + def Stop(self): os.killpg(self._proc.pid, signal.SIGTERM) - if self.WaitTimeout(TERMINATE_TIMEOUT) is None: + if self._WaitTimeout(TERMINATE_TIMEOUT) is None: os.killpg(self._proc.pid, signal.SIGKILL) def _IsSuccessful(self): @@ -187,9 +187,9 @@ def Worker(job_queue): break succ, reason = job.Test() if succ: - Log.Pass(job.name, job.log_filename) + Log.Pass(job.name) else: - Log.Fail(job.name, job.log_filename, reason) + Log.Fail(job.name, reason) class Log(object): @@ -197,17 +197,15 @@ class Log(object): _print_lock = threading.Lock() @staticmethod - def Pass(test_name, log_filename): + def Pass(test_name): with Log._print_lock: - print('\033[32m%r >>> Passed (%s)\033[39m' % (test_name, - log_filename)) + print('\033[32m%r >>> Passed\033[39m' % test_name) @staticmethod - def Fail(test_name, log_filename, reason): + def Fail(test_name, reason): Log._all_pass = False with Log._print_lock: - print('\033[31m%r >>> Failed for %r (%r)\033[39m' % - (test_name, reason, log_filename)) + print('\033[31m%r >>> Failed for %r \033[39m' % (test_name, reason)) @staticmethod def IsAllPass(): @@ -258,7 +256,18 @@ def RunTestings(): JoinWorkers(workers, job_queue) return (0 if Log.IsAllPass else 1) +def SignalTermHandler(signal, frame): + pass + +def SignalIntHandler(signal, frame): + pass + +def HandleSignals(): + signal.signal(signal.SIGTERM, SignalTermHandler) + signal.signal(signal.SIGINT, SignalIntHandler) + def Main(): + HandleSignals() keep_going, exit_code = ParseArgv() if not keep_going: return exit_code diff --git a/meowpp/utility/factory.h b/meowpp/utility/factory.h deleted file mode 100644 index 8e350a0..0000000 --- a/meowpp/utility/factory.h +++ /dev/null @@ -1,383 +0,0 @@ -/*! - * @file factory.h - * @brief Contains a base class for a factory. - * - * A factory contains an array of inputs, an array of outputs and an operation. - * You can update some elements in the array of inputs and then call - * `Factory::Update()` to update the outputs (it will run the operation only if - * some input elements are changed since last update). - * - * @author cathook - */ - -#ifndef __MEOWPP_UTILITY_FACTORY_H__ -#define __MEOWPP_UTILITY_FACTORY_H__ - -#include "operation.h" -#include "pointer.h" -#include "self.h" - - -namespace meow { - -namespace factory_types { - - -/*! - * @brief A base class for kinds of factory classes. - */ -class Base { - protected: - struct BaseData { - Pointer<Operation const> oper; - Pointer<Pointer<Object const>> inputs; - Pointer<Pointer<Object>> outputs; - - //! An array with each elements points to the input elements with - //! non-constant type. - Pointer<Pointer<Object>> non_const_inputs; - - //! An array with each elements points to the output elements with - //! constant type. - Pointer<Pointer<Object const>> const_outputs; - - /*! - * @brief Constructor. - * @param [in] arg_oper The operation this factory should run. - * @param [in] arg_inputs A pointer points to the array of the input - * elements. - * @param [in] arg_outputs A pointer points to the array of the output - * elements. - */ - BaseData(Pointer<Operation const> const& arg_oper, - Pointer<Pointer<Object const>> const& arg_inputs, - Pointer<Pointer<Object>> const& arg_outputs): - oper(arg_oper), - inputs(arg_inputs), - outputs(arg_outputs), - non_const_inputs(new Pointer<Object>[oper->inputs_size()], ARRAY, true), - const_outputs( - new Pointer<Object const>[oper->outputs_size()], ARRAY, true) { - for (int i = 0, i_max = oper->inputs_size(); i < i_max; ++i) { - non_const_inputs[i] = Pointer<Object>( - const_cast<Object*>(inputs[i].address()), SINGLE, false); - } - for (int i = 0, i_max = oper->outputs_size(); i < i_max; ++i) { - const_outputs[i] = Pointer<Object const>( - outputs[i].address(), SINGLE, false); - } - }; - }; - - Base() {} - - public: - virtual ~Base() {} - - /*! - * @brief Updates the output elements by re-run the operation. - */ - virtual State Update() const = 0; - - /*! - * @brief Returns whether the last time calling `Update()` did update really - * or not. - * - * If the input elements were not changed before `Update()` be called, it - * might be returns false (depends on how it implements). - */ - virtual bool HasRedo() const = 0; - - /*! - * @brief Gets the operation. - */ - virtual Pointer<Operation const> operation() const = 0; - - /*! - * @brief Gets the pointer points to the array of input elements. - */ - virtual Pointer<Pointer<Object>> inputs() const = 0; - - /*! - * @brief Gets the pointer points to the array of output elements. - */ - virtual Pointer<Pointer<Object const>> outputs() const = 0; -}; - - -/*! - * An implement of the factory class. - * - * When the function `Update()` be called, no matter whether the input elements - * has changed or not, it will do the operation. - */ -class CheckOff : public Base { - private: - struct Data : Base::BaseData { - Data(Pointer<Operation const> const& arg_oper, - Pointer<Pointer<Object const>> const& arg_inputs, - Pointer<Pointer<Object>> const& arg_outputs) : - BaseData(arg_oper, arg_inputs, arg_outputs) {} - }; - - Self<Data> const self_; - - public: - - /*! - * @brief Constructor. - * @param [in] arg_oper The operation for this factory to run. - * @param [in] arg_inputs A pointer points to the array of the pointer points - * to the input elements. - * @param [in] arg_outputs A pointer points to the array of the pointer points - * to the output elements. - */ - CheckOff(Pointer<Operation const> const& arg_oper, - Pointer<Pointer<Object const>> const& arg_inputs, - Pointer<Pointer<Object>> const& arg_outputs) : - self_(Data(arg_oper, arg_inputs, arg_outputs)) {} - - /*! - * @brief Updates the output elements by running the operation. - */ - State Update() const { - return self_->oper->Operate(self_->inputs, self_->outputs); - } - - /*! - * @brief It will always return true. - */ - bool HasRedo() const { - return true; - } - - /*! - * @brief Gets the operation. - */ - virtual Pointer<Operation const> operation() const { - return self_->oper; - } - - /*! - * @brief Gets the pointer points to the array of input elements. - */ - Pointer<Pointer<Object>> inputs() const { - return self_->non_const_inputs; - } - - /*! - * @brief Gets the pointer points to the array of output elements. - */ - Pointer<Pointer<Object const>> outputs() const { - return self_->const_outputs; - } - -}; - - -/*! - * An implement of the factory class. - * - * It will check whether the input elements has changed before running the - * operation. - */ -class CheckOn : public Base { - private: - struct Data : Base::BaseData { - //! Stores the last input elements which are using to check whether the - //! input elements have changed or not. - Pointer<Pointer<Object>> old_inputs; - - //! Stores the state returned by the operation last time. - State last_state; - - //! Stores whether the last `Update()` run the operation or not. - bool has_redo; - - //! Stores whether it has not run the `Update()` yet. - bool first_time; - - Data(Pointer<Operation const> const& arg_oper, - Pointer<Pointer<Object const>> const& arg_inputs, - Pointer<Pointer<Object>> const& arg_outputs) : - BaseData(arg_oper, arg_inputs, arg_outputs), - old_inputs(new Pointer<Object>[oper->inputs_size()], ARRAY, true), - has_redo(false), - first_time(true) { - for (int i = 0, i_max = oper->inputs_size(); i < i_max; ++i) { - old_inputs[i] = Pointer<Object>(inputs[i]->Copy(), SINGLE, true); - } - } - }; - - Self<Data> const self_; - - public: - - /*! - * @brief Constructor. - * @param [in] arg_oper The operation this factory should run. - * @param [in] arg_inputs A pointer points to the array of the input - * elements. - * @param [in] arg_outputs A pointer points to the array of the output - * elements. - */ - CheckOn(Pointer<Operation const> const& arg_oper, - Pointer<Pointer<Object const>> const& arg_inputs, - Pointer<Pointer<Object>> const& arg_outputs) : - self_(Data(arg_oper, arg_inputs, arg_outputs)) {} - - /*! - * @brief Updates the output elements if needs. - * - * It will check whether the input elements different from the old ones first. - */ - State Update() const { - bool needs_to_update = self_->first_time; - if (!needs_to_update) { - for (int i = 0, i_max = self_->oper->inputs_size(); i < i_max; ++i) { - Object const* old = self_->old_inputs[i].address(); - Object const* cur = self_->inputs[i].address(); - if (!old->Equals(cur)) { - needs_to_update = true; - break; - } - } - } - if (!needs_to_update) { - self_()->has_redo = false; - } else { - for (int i = 0, i_max = self_->oper->inputs_size(); i < i_max; ++i) { - Object* old = self_->old_inputs[i].address(); - Object const* cur = self_->inputs[i].address(); - old->CopyFrom(cur); - } - self_()->last_state = self_->oper->Operate(self_->inputs, self_->outputs); - self_()->has_redo = true; - } - self_()->first_time = false; - return self_->last_state; - } - - /*! - * @brief Returns whether the output elements have been re-generated by - * running the operation again. - * - * It will check whether the input elements different from the old ones first. - */ - bool HasRedo() const { - return self_->has_redo; - } - - /*! - * @brief Gets the operation. - */ - virtual Pointer<Operation const> operation() const { - return self_->oper; - } - - /*! - * @brief Gets the array of the input elements. - */ - Pointer<Pointer<Object>> inputs() const { - return self_->non_const_inputs; - } - - /*! - * @brief Gets the array of the output elements. - */ - Pointer<Pointer<Object const>> outputs() const { - return self_->const_outputs; - } - -}; - -} // factory_types - - -/*! - * @brief A class which contains input elements, output elements and an - * operation. - */ -class Factory : public Object { - private: - - Pointer<factory_types::Base> factory_; - - public: - - /*! - * @brief Constructor. - */ - Factory(Pointer<Operation const> const& arg_oper, - Pointer<Pointer<Object const>> const& arg_inputs, - Pointer<Pointer<Object>> const& arg_outputs, - bool arg_check_before_update) { - if (arg_check_before_update) { - factory_ = Pointer<factory_types::Base>( - new factory_types::CheckOn( - arg_oper, arg_inputs, arg_outputs), SINGLE, true); - } else { - factory_ = Pointer<factory_types::Base>( - new factory_types::CheckOff( - arg_oper, arg_inputs, arg_outputs), SINGLE, true); - } - } - - /*! - * @brief Updates the output elements. - */ - State Update() const { - return factory_->Update(); - } - - /*! - * @brief Returns whether the output elements have been re-generated by - * running the operation again. - */ - bool HasRedo() const { - return factory_->HasRedo(); - } - - /*! - * @brief Gets the operation. - */ - Pointer<Operation const> operation() const { - return factory_->operation(); - } - - /*! - * @brief Gets the array of the input elements. - */ - Pointer<Pointer<Object>> inputs() const { - return factory_->inputs(); - } - - /*! - * @brief Gets the array of the output elements. - */ - Pointer<Pointer<Object const>> outputs() const { - return factory_->outputs(); - } - - Object* Copy() const { - return NULL; - } - - Object* CopyFrom(Object const* another_factory) { - return NULL; - } - - bool Equals(Object const* another_factory) { - return false; - } - -#ifdef MEOWPP_UTILITY_FACTORY_TESTING - friend class FactoryTest; -#endif - -}; - -} // meow - -#endif // __MEOWPP_UTILITY_FACTORY_H__ diff --git a/meowpp/utility/factory_test.cpp b/meowpp/utility/factory_test.cpp deleted file mode 100644 index 98ef2b0..0000000 --- a/meowpp/utility/factory_test.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#define MEOWPP_UTILITY_FACTORY_TESTING - -#include <cstdio> - -#include <meowpp/utility/factory.h> -#include <meowpp/utility/factory.h> -#include <meowpp/utility/factory.h> -#include <meowpp/utility/factory.h> - -#include <meowpp/debug/assert.h> - -#include <meowpp/utility/object.h> -#include <meowpp/utility/operation.h> - - -namespace meow { - -class FactoryTest { - private: - int f() { return 123; } - class Oper1 : public Operation { - public: - Oper1() : Operation(2, 4) {} - State Operate(Pointer<Object const> const* inputs, - Pointer<Object> const* outputs) const { - Int32 a = *(Int32 const*)inputs[0].address(); - Int32 b = *(Int32 const*)inputs[1].address(); - outputs[0]->CopyFrom(&a); - outputs[1]->CopyFrom(&b); - outputs[2]->CopyFrom(&b); - outputs[3]->CopyFrom(&a); - return (a < b ? 0 : 1); - } - }; - public: - FactoryTest() { - Pointer<Int32>* in = new Pointer<Int32>[2]; - Pointer<Int32 const>* out = new Pointer<Int32 const>[4]; - Pointer<Object const>* in2 = new Pointer<Object const>[2]; - Pointer<Object>* out2 = new Pointer<Object>[4]; - for (int i = 0; i < 2; ++i) { - in[i] = Pointer<Int32>(new Int32, SINGLE, true); - in2[i] = Pointer<Object const>(in[i].address(), SINGLE, false); - } - for (int i = 0; i < 4; ++i) { - out[i] = Pointer<Int32 const>(new Int32, SINGLE, true); - out2[i] = Pointer<Object>( - const_cast<Int32*>(out[i].address()), SINGLE, false); - } - fprintf(stderr, "aaaaaaaaa\n"); - Oper1 op; - int kk; - kk = f(); - fprintf(stderr, "kk = %d\n", kk); - fprintf(stderr, "mid0\n"); - { - Factory f1(Pointer<Operation const>(&op, SINGLE, false), - Pointer<Pointer<Object const>>(in2, ARRAY, false), - Pointer<Pointer<Object>>(out2, ARRAY, false), - false); - Assert(f1.operation() == &op, ""); - *in[0] = 3; - *in[1] = 5; - kk = static_cast<int>(f1.Update()); - Assert(kk == 0, ""); - Assert(*in[0] == 3, ""); - Assert(*in[1] == 5, ""); - Assert(*out[0] == 3, ""); - Assert(*out[1] == 5, ""); - Assert(*out[2] == 5, ""); - Assert(*out[3] == 3, ""); - Assert(f1.HasRedo() == true, ""); - Assert(f1.operation() == &op, ""); - *in[0] = 7; - *in[1] = 5; - kk = static_cast<int>(f1.Update()); - Assert(kk == 1, ""); - Assert(*in[0] == 7, ""); - Assert(*in[1] == 5, ""); - Assert(*out[0] == 7, ""); - Assert(*out[1] == 5, ""); - Assert(*out[2] == 5, ""); - Assert(*out[3] == 7, ""); - Assert(f1.HasRedo() == true, ""); - Assert(f1.operation() == &op, ""); - *in[0] = 7; - *in[1] = 5; - kk = static_cast<int>(f1.Update()); - Assert(kk == 1, ""); - Assert(*in[0] == 7, ""); - Assert(*in[1] == 5, ""); - Assert(*out[0] == 7, ""); - Assert(*out[1] == 5, ""); - Assert(*out[2] == 5, ""); - Assert(*out[3] == 7, ""); - Assert(f1.HasRedo() == true, ""); - Assert(f1.operation() == &op, ""); - fprintf(stderr, "hi!!\n"); - } - fprintf(stderr, "====\n"); - { - Factory f2(Pointer<Operation const>(&op, SINGLE, false), - Pointer<Pointer<Object const>>(in2, ARRAY, false), - Pointer<Pointer<Object>>(out2, ARRAY, false), - true); - Assert(f2.operation() == &op, ""); - Assert(f2.HasRedo() == false, ""); - *in[0] = 3; - *in[1] = 5; - kk = static_cast<int>(f2.Update()); - Assert(f2.HasRedo() == true, ""); - Assert(kk == 0, "kk = %d\n", kk); - Assert(*in[0] == 3, ""); - Assert(*in[1] == 5, ""); - Assert(*out[0] == 3, ""); - Assert(*out[1] == 5, ""); - Assert(*out[2] == 5, ""); - Assert(*out[3] == 3, ""); - Assert(f2.operation() == &op, ""); - *in[0] = 7; - *in[1] = 5; - kk = static_cast<int>(f2.Update()); - Assert(f2.HasRedo() == true, ""); - Assert(kk == 1, ""); - Assert(*in[0] == 7, ""); - Assert(*in[1] == 5, ""); - Assert(*out[0] == 7, ""); - Assert(*out[1] == 5, ""); - Assert(*out[2] == 5, ""); - Assert(*out[3] == 7, ""); - Assert(f2.operation() == &op, ""); - *in[0] = 7; - *in[1] = 5; - kk = static_cast<int>(f2.Update()); - Assert(f2.HasRedo() == false, ""); - Assert(kk == 1, ""); - Assert(*in[0] == 7, ""); - Assert(*in[1] == 5, ""); - Assert(*out[0] == 7, ""); - Assert(*out[1] == 5, ""); - Assert(*out[2] == 5, ""); - Assert(*out[3] == 7, ""); - Assert(f2.operation() == &op, ""); - fprintf(stderr, "hi\n"); - } - fprintf(stderr, "mid\n"); - - delete [] in; - delete [] out; - delete [] in2; - delete [] out2; - fprintf(stderr, "hj\n"); - } -} _; - -} - -int main() { - return 0; -} - diff --git a/meowpp/utility/object.h b/meowpp/utility/object.h deleted file mode 100644 index 7c665a0..0000000 --- a/meowpp/utility/object.h +++ /dev/null @@ -1,77 +0,0 @@ -/*! - * @file object.h - * @brief Contains a base class for most of all the classes in meowpp. - * - * @author cathook - */ - -#ifndef __MEOWPP_UTILITY_OBJECT_H__ -#define __MEOWPP_UTILITY_OBJECT_H__ - -#include <cstdlib> - -namespace meow { - - -/*! - * @brief The base class. - */ -class Object { - protected: - - /*! - * @brief A protected constructor to prevent developers create an instance of - * Object directly. - */ - Object() {} - - /*! - * @brief Disable the copy operation. - */ - Object(Object const& b); - - public: - - /*! - * @brief Virtual destructor. - */ - virtual ~Object() {} - - /*! - * @brief Creates a copy of itself and return the pointer to it. - * - * It will return NULL in default if you don't implement it. - */ - virtual Object* Copy() const { - return NULL; - } - - /*! - * @brief Copies data from another object. - * @param [in] ptr Points to another object. - * - * It will return NULL in default if you don't implement it. - */ - virtual Object* CopyFrom(Object const* ptr) { - return NULL; - } - - /*! - * @brief Returns whether it equals to another object or not. - * @param [in] ptr Points to another object. - * - * It will always return false if you don't implement it. - */ - virtual bool Equals(Object const* ptr) const { - return false; - } - - /*! - * @brief Disable the copy operator. - */ - Object& operator=(Object const& b); -}; - -} // meow - -#endif // __MEOWPP_UTILITY_OBJECT_H__ diff --git a/meowpp/utility/pointer.h b/meowpp/utility/pointer.h deleted file mode 100644 index 3b4d08d..0000000 --- a/meowpp/utility/pointer.h +++ /dev/null @@ -1,174 +0,0 @@ -/*! - * @file pointer.h - * @brief Contains a pointer class which has a counter-mechanism to prevent - * memory leak. - * - * @author cathook - */ - -#ifndef __MEOWPP_UTILITY_POINTER_H__ -#define __MEOWPP_UTILITY_POINTER_H__ - -#include <cstddef> -#include <cstdlib> - -#include "object.h" - -namespace meow { - - -/*! - * @brief Types of pointer. - */ -enum PointerType { - SINGLE = 0, - ARRAY = 1 -}; - - -/*! - * @brief A pointer points to the template `Type`. - */ -template<typename Type> -class Pointer : public Object { - private: - struct RealPointer { - Type* address; - - //! The type of the address. - PointerType type; - - //! Whether the address should be deleted when no one points to it. - bool auto_delete; - - //! Stores number of pointers point to it. - int counter; - - RealPointer(Type* arg_address, - PointerType arg_type, - bool arg_auto_delete, - int arg_counter) : - address(arg_address), - type(arg_type), - auto_delete(arg_auto_delete), - counter(arg_counter) {} - - ~RealPointer() { - if (auto_delete) { - switch (type) { - case SINGLE: - delete address; - break; - case ARRAY: - delete [] address; - break; - } - } - } - }; - - RealPointer* ptr_; - - void Attach(RealPointer* arg_ptr2) { - ptr_ = arg_ptr2; - ptr_->counter += 1; - } - - void Detach() { - ptr_->counter -= 1; - if (ptr_->counter == 0) { - delete ptr_; - } - } - public: - /*! - * @brief Default constructor, let the pointer points to NULL. - */ - Pointer() : Pointer(NULL, SINGLE, false) {} - - /*! - * @brief Copy constructor. - */ - Pointer(Pointer const& arg_ptr) { - Attach(arg_ptr.ptr_); - } - - /*! - * @brief Constructor with gived address to point. - * - * If `arg_auto_delete` is `true`, it will automatically delete it when - * there are no instance of Pointer\<Type\> points to that `address` - * - * @param [in] arg_address Points to the address. - * @param [in] arg_type - * @param [in] arg_auto_delete - */ - Pointer(Type* arg_address, PointerType arg_type, bool arg_auto_delete) : - ptr_(new RealPointer(arg_address, arg_type, arg_auto_delete, 1)) {} - - /*! - * @brief Destructor. - */ - ~Pointer() { Detach(); } - - /*! - * @brief Gets whether it will delete the address automatically or not. - */ - bool auto_delete() const { - return ptr_->auto_delete; - } - - /*! - * @brief Gets the address it points to. - */ - Type* address() const { - return ptr_->address; - } - - /*! - * @brief Same as `address()` - */ - operator Type*() const { - return address(); - } - - /*! - * @brief Gets the pointer points to the body. - */ - Type* operator->() const { - return address(); - } - - /*! - * @brief Points to another instance of Pointer. - */ - Pointer& operator=(Pointer const& b) { - Detach(); - Attach(b.ptr_); - return *this; - } - - Object* Copy() const { - return new Pointer(*this); - } - - Object* CopyFrom(Object const* another_pointer) { - (*this) = *dynamic_cast<Pointer const*>(another_pointer); - return this; - } - - bool Equals(Object const* another_pointer) { - return (ptr_->address == - dynamic_cast<Pointer const*>(another_pointer)->ptr_->address); - } - -#ifdef MEOWPP_UTILITY_POINTER_TESTING - friend class PointerTest; -#endif // MEOWPP_UTILLITY_POINTER_TESTING - -}; - -} // meow - -#endif // __MEOWPP_UTILITY_POINTER_H__ - diff --git a/meowpp/utility/pointer_test.cpp b/meowpp/utility/pointer_test.cpp deleted file mode 100644 index b2ecb52..0000000 --- a/meowpp/utility/pointer_test.cpp +++ /dev/null @@ -1,130 +0,0 @@ -#define MEOWPP_UTILITY_POINTER_TESTING - -#include <meowpp/utility/pointer.h> -#include <meowpp/utility/pointer.h> -#include <meowpp/utility/pointer.h> -#include <meowpp/debug/assert.h> - -namespace meow { -class PointerTest { - private: - struct Data { - int* info; - int some_data; - Data(Data const& b) : info(b.info), some_data(b.some_data) {} - Data(int* infoo) : info(infoo) {} - ~Data() { (*info)++; } - }; - public: - PointerTest() { - { - Pointer<Data> ptr1; - Assert(ptr1.ptr_->address == NULL, ""); - Assert(ptr1.ptr_->auto_delete == false, ""); - } - int remove = 0; - Data some_data(&remove); - Pointer<Data> ptr0(&some_data, SINGLE, false); - { - Pointer<Data> ptr2(&some_data, SINGLE, false); - Assert(ptr2.ptr_->address == &some_data, ""); - Assert(ptr2.ptr_->auto_delete == false, ""); - { - Pointer<Data> ptr5(ptr2); - Assert(ptr2.ptr_ == ptr2.ptr_, ""); - } - Assert(remove == 0, ""); - } - Assert(remove == 0, ""); - { - Data* ptrr = new Data(&remove); - Assert(ptrr->info == &remove, ""); - Pointer<Data> ptr3(ptrr, SINGLE, true); - Assert(remove == 0, ""); - Assert(ptr3.ptr_->address == ptrr, ""); - Assert(remove == 0, ""); - Assert(ptrr->info == &remove, ""); - Assert(remove == 0, ""); - Assert(ptr3.ptr_->address->info == &remove, ""); - Assert(ptr3.ptr_->auto_delete == true, ""); - Assert(ptr3.address() == ptr3.ptr_->address, ""); - Assert(ptr3.auto_delete() == ptr3.ptr_->auto_delete, ""); - Assert(ptr3.ptr_->address->info == &remove, - "%llu --- %llu\n", - (unsigned long long)(ptr3.ptr_->address->info), - (unsigned long long)(&remove)); - Assert(ptr3.ptr_->counter == 1, ""); - { - Pointer<Data> ptr6(ptr3); - Assert(ptr3.ptr_ == ptr3.ptr_, ""); - Assert(ptr6.ptr_->counter == 2, ""); - } - Assert(ptr3.ptr_->counter == 1, ""); - Assert(remove == 0, ""); - } - Assert(remove == 1, " r = %d\n", remove); - { - Data* ptr = new Data(&remove); - remove = 0; - { - Pointer<Data> ptrr(ptr, SINGLE, false); - } - Assert(remove == 0, ""); - delete ptr; - } - Assert(remove == 1, ""); - (*ptr0).some_data = 0; - Assert(some_data.some_data == 0, ""); - Assert(some_data.some_data == (*ptr0).some_data, ""); - ptr0[0].some_data = 1; - Assert(some_data.some_data == 1, ""); - Assert(some_data.some_data == (*ptr0).some_data, ""); - ptr0->some_data = 2; - Assert(some_data.some_data == 2, ""); - Assert(some_data.some_data == (*ptr0).some_data, ""); - Pointer<Data> ptr7; - ptr7 = ptr0; - Assert(ptr7.ptr_ == ptr0.ptr_, ""); - { - Pointer<int> k(new int, SINGLE, true); - Pointer<int> l(new int, SINGLE, true); - (*k) = 3; - (*l) = 3; - Assert(*k == 3, ""); - Assert(*l == 3, ""); - Assert(!k.Equals(&l), ""); - Pointer<int> kk(k); - Assert(k.Equals(&kk), ""); - Assert(k.ptr_->counter == 2, ""); - *l = 10; - k.CopyFrom(&l); - Assert(k.ptr_->counter == 2, ""); - Assert(kk.ptr_->counter == 1, ""); - Assert(l.ptr_->counter == 2, ""); - Assert(k.Equals(&l), ""); - Assert((*k) == 10, ""); - Pointer<int>* cp = dynamic_cast<Pointer<int>*>(k.Copy()); - Assert((*cp) == k, ""); - Assert(cp->ptr_ == k.ptr_, ""); - Assert(cp->ptr_->counter == 3, ""); - delete cp; - Assert(k.ptr_->counter == 2, ""); - } - Pointer<int>* in = new Pointer<int>[2]; - Assert(in[0].ptr_ != NULL, ""); - Assert(in[1].ptr_ != NULL, ""); - Assert(in[0].ptr_ != in[1].ptr_, ""); - Assert(in[0].ptr_->counter == 1, ""); - Assert(in[1].ptr_->counter == 1, ""); - Assert(in[0].ptr_->auto_delete == false, ""); - Assert(in[1].ptr_->auto_delete == false, ""); - Assert(in[0].ptr_->address == NULL, ""); - Assert(in[1].ptr_->address == NULL, ""); - delete [] in; - Pointer<int> ptr(new int[2], ARRAY, true); - }; -} _; - -} - -int main() { return 0; } diff --git a/meowpp/utility/self.h b/meowpp/utility/self.h deleted file mode 100644 index a98a53c..0000000 --- a/meowpp/utility/self.h +++ /dev/null @@ -1,244 +0,0 @@ -/*! - * @file self.h - * @brief Contains a class for implementing a reference mechanism which allows - * variables referencing to another object anytime. - * - * @author cathook - */ - -#ifndef __MEOWPP_UTILITY_SELF_H__ -#define __MEOWPP_UTILITY_SELF_H__ - -#include <cstdlib> - -namespace meow { - - -/*! - * @brief An implementation for the reference mechanism. - * - * Some example code: - * @code{.cpp} - * #include <meowpp/utility/self.h> - * - * class A { - * private: - * - * // Data members of this class. You can also move the methods of class A - * // into this structure if you like. - * struct DataMember { - * int var1; - * int var2; - * int counter; - * - * // The constructor "Self<DataMember>::Self()" will call the constructor - * // "DataMember::DataMember()" - * DataMember() : var1(0), var2(0), counter(0) {} - * - * // The constructor "Self<DataMember>::Self(DataMember const&)" will call - * // the constructor "DataMember::DataMember(DataMember const&)" - * DataMember(DataMember const& b) : - * var1(b.var1), var2(b.var2), counter(0) {} - * - * // Customize constructor. - * DataMember(int var1_init_value) : - * var1(var1_init_value), var2(0), counter(0) {} - * - * // Destructor, will be called when nobody references to it. - * ~DataMember() {} - * - * // The "Self<DataMember>::CopyFrom(Self<DataMember> const&)" will call - * // "DataMember::CopyFrom(DataMember const&)" to copy data. - * DataMember const& CopyFrom(DataMember const& b) { - * var1 = b.var1; - * var2 = b.var2; - * } - * }; - * - * meow::Self<DataMember> const self_; // Use constant type to reduce the - * // protential error caused by typing - * // wrong. - * - * public: - * - * // Here the "Self<DataMember>::Self()" will be called. - * A() {} - * - * // Notice! "Self<DataMember>::Self(Self const& another_self)" will let - * // itself reference to the gived object instead of creating a new one. - * // So here the copy constructor will not copy from the gived object, it - * // will just reference from it. - * A(A const& another_class_a) : self_(another_class_a.self_) {} - * - * // Customize constructor. - * A(int var1_init_value) : self_(DataMember(var1_init_value)) {} - * - * // You don't need to call some extra function in destructor, because - * // there's a counter in the Self class. - * ~A() {} - * - * // A constant method. - * int GetVar1() const { - * return self_->var1; // Use the operator "->" to access the address of - * // the DataMember. Because we declare self_ be a - * // constant type, here "self_->var1" will also be - * // a constant. - * } - * - * // A non-constant method. - * void SetVar1(int new_value) { - * int old_value = self_->var1; - * self_()->var1 = new_value; // Use operator "()" (i.e. self_()) to - * // access the object with non-constant type, - * // so here "self_()->var1" will be a - * // non-constant variable. - * if (old_value != new_value) { - * self_()->var2 = old_value; - * //self_->var2 = old_value; // !! It cause an error because operator "()" - * // is missed if you want to modify the member - * // in the DataMember. - * } - * } - * - * int GetVar2() const { - * self_()->counter += 1; // !! It will not cause an error. Actually, - * // the "const" keyword of a method will become - * // more meanless, because inside the method, you - * // can just use self_-> or self_()-> to determind - * // whether you want to access the DataMember in - * // constant mode or not. It might be dangerous - * // but giving developer more flexable. - * return self_->var2; - * } - * }; - * @endcode - */ -template<typename SelfType> -class Self { - private: - struct Body { - int counter; - SelfType body; - - Body() : counter(1) {} - Body(SelfType const& arg_another_body) : - counter(1), body(arg_another_body) {} - }; - - Body* body_; - - /*! - * @brief Attaches to another body. - * @param arg_another_body Pointer to another body. - */ - void Attach(Body* arg_another_body) { - body_ = arg_another_body; - body_->counter += 1; - } - - /*! - * @brief Detaches from the current body. - */ - void Detach() { - body_->counter -= 1; - if (body_->counter == 0) { - delete body_; - } - } - public: - /*! - * @brief Creates a new one. - */ - Self() : body_(new Body()) {} - - /*! - * @brief Creates a new one with specifying a initial value for SelfType - * object. - * @param arg_body The initial value of the SelfType object. - */ - Self(SelfType const& arg_body) : body_(new Body(arg_body)) {} - - /*! - * @brief References from another one. - * - * @param arg_another_self Another Self object. - */ - Self(Self const& arg_another_self) { Attach(arg_another_self.body_); } - - /*! - * @brief Detatches. - * - * It will automatically clear the SelfType object when no one refernece from - * it. - */ - ~Self() { Detach(); } - - /*! - * @brief References from another Self object. - * @param arg_another_self Another Self object. - */ - Self const& ReferenceFrom(Self const& arg_another_self) { - Detach(); - Attach(arg_another_self.body_); - return *this; - } - - /*! - * @brief Copies the data in SelfType object from another Self object. - * @param arg_another_self Another Self object. - */ - Self const& CopyFrom(Self const& arg_another_self) { - body_->body.CopyFrom(arg_another_self.body_->body); - return *this; - } - - /*! - * @brief Creates a copy one of itself. - */ - Self Copy() const { return Self(body_->body); } - - /*! - * @brief Checks whether the gived instance of Self references - * from the same SelfType with me or not. - * @param arg_another_self Another instance of Self. - * @return true if we references from the same thing. - */ - bool Is(Self const& arg_another_self) const { - return (body_ == arg_another_self.body_); - } - - /*! - * @brief Access the instance of SelfType by address - */ - SelfType* operator->() { - return &(body_->body); - } - - /*! - * @brief Access the instance of SelfType by constant address - */ - SelfType const* operator->() const { - return &(body_->body); - } - - /*! - * @brief Access itself in non-constant mode. - */ - Self& operator()() const { - return *const_cast<Self*>(this); - } - - /*! - * @brief Disallows the "operator=" so develops need to explicitly use - * CopyFrom/RefernceFrom. - */ - Self& operator=(Self const& b); - -#ifdef MEOWPP_UTILITY_SELF_TESTING - friend class SelfTest; -#endif // MEOWPP_UTILITY_SELF_TESTING -}; - -} // meow - -#endif // __MEOWPP_UTILITY_SELF_H__ diff --git a/meowpp/utility/self_test.cpp b/meowpp/utility/self_test.cpp deleted file mode 100644 index 4756f0f..0000000 --- a/meowpp/utility/self_test.cpp +++ /dev/null @@ -1,188 +0,0 @@ -#define MEOWPP_UTILITY_SELF_TESTING - -#include <meowpp/debug/assert.h> -#include <meowpp/debug/assert.h> -#include <meowpp/debug/assert.h> -#include <meowpp/utility/self.h> - -#include <cstdlib> - -static int counter = 0; - -class A { - private: - struct DataMember { - int var1; - int var2; - int counter; - DataMember() : var1(0), var2(0), counter(0) { - ++counter; - } - DataMember(DataMember const& b) : - var1(b.var1), var2(b.var2), counter(0) { - ++counter; - } - DataMember(int var1_init_value) : - var1(var1_init_value), var2(0), counter(0) { - ++counter; - } - - ~DataMember() { - --counter; - } - - DataMember const& CopyFrom(DataMember const& b) { - var1 = b.var1; - var2 = b.var2; - return *this; - } - }; - - meow::Self<DataMember> const self_; - - public: - A() {} - A(A const& another_class_a) : self_(another_class_a.self_) {} - A(int var1_init_value) : self_(DataMember(var1_init_value)) {} - ~A() {} - - int GetVar1() const { - return self_->var1; - } - - void SetVar1(int new_value) { - int old_value = self_->var1; - self_()->var1 = new_value; - if (old_value != new_value) { - self_()->var2 = old_value; - } - } - - int GetVar2() const { - self_()->counter += 1; - return self_->var2; - } - - bool operator==(A const& b) const { - return (self_->var1 == b.self_->var1 && self_->var2 == b.self_->var2); - } - - bool Is(A const& b) const { - return (self_.Is(b.self_)); - } -}; - -namespace meow { -class SelfTest { - private: - struct Data { - int a, b, *c; - Data() : a(1), b(2), c(NULL) {} - Data(int aa, int bb, int* cc) : a(aa), b(bb), c(cc) {} - ~Data() { - if (c) ++(*c); - } - Data& CopyFrom(Data const& x) { - if (c) (*c) += 100; - a = x.a; - b = x.b; - c = x.c; - return *this; - } - bool operator==(Data const& x) const { - return (a == x.a && b == x.b && c == x.c); - } - bool operator!=(Data const& x) const { - return (!(*this == x)); - } - }; - public: - bool test() { - int remove; - { - Self<Data> s1, s2(Data(2, 3, &remove)); - Assert(s1.body_ != NULL, ""); - Assert(s1.body_->counter == 1, ""); - Assert(s1.body_->body == Data(), ""); - Assert(s2.body_ != NULL, ""); - Assert(s2.body_->counter == 1, ""); - Assert(s2.body_->body != Data(), ""); - Assert(s2.body_->body == Data(2, 3, &remove), ""); - Self<Data> s3(s1); - Assert(s3.body_ == s1.body_, ""); - Assert(s3.body_->counter == 2, ""); - Assert(s3.body_->body == Data(), ""); - Self<Data> s4(s2.Copy()); - Assert(s2.body_ != NULL, ""); - Assert(s2.body_->counter == 1, ""); - Assert(s2.body_->body != Data(), ""); - Assert(s2.body_->body == Data(2, 3, &remove), ""); - Assert(s2.body_ != s4.body_, ""); - Assert(s4.body_ != NULL, ""); - Assert(s4.body_->counter == 1, ""); - Assert(s4.body_->body != Data(), ""); - Assert(s4.body_->body == Data(2, 3, &remove), ""); - Assert(s4.body_->body.c == &remove, ""); - Assert(s3.Is(s1) && s1.Is(s3), ""); - Assert((!s2.Is(s4)) && (!s4.Is(s2)), ""); - Self<Data> s5(s2); - remove = 0; - s2.ReferenceFrom(s1); - Assert(s2.body_ != s5.body_, ""); - Assert(s2.Is(s1), ""); - Assert(remove == 0, ""); - Assert(s2.body_ == s1.body_, ""); - Assert(s3.body_->counter == 3, ""); - Assert(s3.body_->body == Data(), ""); - remove = 0; - Assert(s4.body_->body.c == &remove, ""); - s4.CopyFrom(s1); - Assert(s4.body_->body.c != &remove, ""); - Assert(remove == 100, "remove = %d\n", remove); - Assert(s4.body_ != s1.body_, ""); - Assert(s4.body_->counter == 1, ""); - Assert(s4.body_->body == s1.body_->body, ""); - Assert(!(s4.Is(s1)), ""); - s4.Attach(s4.body_); - Assert(s4.body_->counter == 2, ""); - s4.Attach(s4.body_); - Assert(s4.body_->counter == 3, ""); - s4.Detach(); - s4.Detach(); - Assert(s4.body_->counter == 1, ""); - remove = 0; - printf("%llu %d\n", (unsigned long long)s1.body_, s1.body_->counter); - printf("%llu %d\n", (unsigned long long)s2.body_, s2.body_->counter); - printf("%llu %d\n", (unsigned long long)s3.body_, s3.body_->counter); - printf("%llu %d\n", (unsigned long long)s4.body_, s4.body_->counter); - printf("%llu %d\n", (unsigned long long)s5.body_, s5.body_->counter); - s1.body_->body.c = &remove; - s4.body_->body.c = &remove; - s5.body_->body.c = &remove; - } - Assert(remove == 3, "remove = %d\n", remove); - return true; - } -}; -} - -int main() { - bool ok = true; - { - A a1; - A a2(a1); - a2.SetVar1(123); - if (!(a1 == a2)) { - ok = false; - } - A a3(32); - } - if (counter != 0) { - ok = false; - } - if (ok) { - meow::SelfTest tester; - tester.test(); - } - return ok ? 0 : 1; -} |