Very interesting and helpful answers, thank you.
I decided to see how many times i could shot myself in the foot.
Basically if i could make something that was lighter then the suggestions.
That is without std::shared_ptr and std::function.
And yet again i managed to get into a fight with the compiler
This is sandbox code so expect strangeness and inconsistencies:
#include <windows.h> // UNREFERENCED_PARAMETER
#include <assert.h>
#include <cstddef>
#include <utility>
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// default_delete - scalar
template<typename T>
struct default_delete
{
inline void operator()(T obj) const
{
// empty
}
};
// default_delete - scalar pointer
template<typename T>
struct default_delete<T*>
{
inline void operator()(T *ptr) const
{
delete ptr;
}
};
// default_delete - array
template<typename T>
struct default_delete<T[]>
{
inline void operator()(T *ptr) const
{
delete [] ptr;
}
};
template<typename T>
struct default_empty {
inline void operator()(T) const {
// empty
}
};
// scalar version
template<typename T, typename OnLeaveScopeFunc>
class scoped {
public:
explicit scoped(T p = 0, OnLeaveScopeFunc func = default_delete<T>) : m_obj(p), m_onLeaveScope(func) {}
~scoped(void)
{
m_onLeaveScope(m_obj);
}
// Common to both pointer and array
void reset(T p = 0)
{
if (m_obj != p)
{
m_onLeaveScope(m_obj);
m_obj = p;
}
}
T get(void) const
{
return m_obj;
}
bool operator==(const T p) const
{
return m_obj == p;
}
bool operator!=(const T p) const
{
return !(m_obj == p);
}
// Move constructor and assignment
// This will enable make_scoped to work
scoped(scoped &&right)
{
m_obj = right.m_obj;
m_onLeaveScope = right.m_onLeaveScope;
right.m_obj = 0;
}
scoped &operator=(scoped &&right)
{
scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
return *this;
}
void swap(scoped &right)
{
std::swap(m_obj, right.m_obj);
std::swap(m_onLeaveScope, right.m_onLeaveScope);
}
T release(void)
{
T temp = m_obj;
m_obj = 0;
return temp;
}
private:
T m_obj;
OnLeaveScopeFunc m_onLeaveScope;
DISALLOW_COPY_AND_ASSIGN(scoped);
};
// scalar pointer version
template<typename T, typename OnLeaveScopeFunc>
class scoped<T*, OnLeaveScopeFunc> {
public:
typedef T element_type;
explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}
~scoped(void)
{
m_onLeaveScope(m_ptr);
}
// Common to both pointer and array
void reset(T *p = nullptr)
{
if (m_ptr != p)
{
m_onLeaveScope(m_ptr);
m_ptr = p;
}
}
T *get(void) const
{
return m_ptr;
}
bool operator==(const T *p) const
{
return m_ptr == p;
}
bool operator!=(const T *p) const
{
return !(m_ptr == p);
}
// Move constructor and assignment
// This will enable make_scoped to work
scoped(scoped &&right)
{
m_ptr = right.m_ptr;
m_onLeaveScope = right.m_onLeaveScope;
right.m_ptr = nullptr;
right.m_onLeaveScope = m_onLeaveScope_empty;
}
scoped &operator=(scoped &&right)
{
scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
return *this;
}
void swap(scoped &right)
{
std::swap(m_ptr, right.m_ptr);
std::swap(m_onLeaveScope, right.m_onLeaveScope);
}
T *release(void)
{
T *temp = m_ptr;
m_ptr = nullptr;
return temp;
}
// Scalar pointer version
T &operator*(void) const
{
assert(m_ptr != nullptr);
return *m_ptr;
}
T *operator->(void) const
{
assert(m_ptr != nullptr);
return m_ptr;
}
private:
T *m_ptr;
OnLeaveScopeFunc m_onLeaveScope;
OnLeaveScopeFunc m_onLeaveScope_empty;
DISALLOW_COPY_AND_ASSIGN(scoped);
};
// array version
// FIXME: Any way to prevent missing [] from compiling ? Ex. scoped<int> bla(new int[])
// or to detect it based on how it is used further down the code ?
template<typename T, typename OnLeaveScopeFunc>
class scoped<T[], OnLeaveScopeFunc> {
public:
typedef T element_type;
explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func) {}
~scoped(void)
{
m_onLeaveScope(m_ptr);
}
// Common to both pointer and array
void reset(T *p = nullptr)
{
if (m_ptr != p)
{
m_onLeaveScope(m_ptr);
m_ptr = p;
}
}
T *get(void) const
{
return m_ptr;
}
bool operator==(const T *p) const
{
return m_ptr == p;
}
bool operator!=(const T *p) const
{
return !(m_ptr == p);
}
// Move constructor and assignment
// This will enable make_scoped to work
scoped(scoped &&right)
{
m_ptr = right.m_ptr;
m_onLeaveScope = right.m_onLeaveScope;
right.m_ptr = nullptr;
}
scoped &operator=(scoped &&right)
{
scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
return *this;
}
void swap(scoped &right)
{
std::swap(m_ptr, right.m_ptr);
std::swap(m_onLeaveScope, right.m_onLeaveScope);
}
T *release(void)
{
T *temp = m_ptr;
m_ptr = nullptr;
return temp;
}
// Array version
// Should we use uintptr_t instead of ptrdiff_t ?
T &operator[](std::ptrdiff_t i) const
{
assert(i >= 0);
assert(m_ptr != nullptr);
return m_ptr[i];
}
private:
T *m_ptr;
OnLeaveScopeFunc m_onLeaveScope;
DISALLOW_COPY_AND_ASSIGN(scoped);
};
// template argument deduction goodness
template <typename T, typename F>
inline scoped<T, F> make_scoped(T t, F f)
{
return std::move(scoped<T, F>(t, f));
}
//----------------------------------------------------
inline void normal_f(int p) {
_tprintf_s(_T("normal_f p: %d\n"), p);
}
struct normal_struct_f {
inline void operator()(int p) const {
_tprintf_s(_T("normal_struct_f p: %d\n"), p);
}
};
inline void normal_fp(int *p) {
_tprintf_s(_T("normal_fp *p: %d\n"), *p);
}
struct normal_struct_fp {
inline void operator()(int *p) const {
_tprintf_s(_T("normal_struct_fp *p: %d\n"), *p);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
// 2010-02-24
auto f = [](int p) {
_tprintf_s(_T("f p: %d\n"), p);
};
auto fp = [](int *p) {
_tprintf_s(_T("fp *p: %d\n"), *p);
};
{
auto t1 = make_scoped(1975, f);
auto t2 = make_scoped(1975, normal_f);
auto t3 = make_scoped(1975, normal_struct_f());
}
_tprintf_s(_T("\n")); // For output readability
{
auto t1 = make_scoped(new int(1975), fp);
auto t2 = make_scoped(new int(1975), normal_fp);
auto t3 = make_scoped(new int(1975), normal_struct_fp());
}
_tprintf_s(_T("\n")); // For output readability
return 0;
}
Ok, so the problem i'm having:
In <T*> version of 'scoped(scoped &&right)' (that's the class i've used as test subject)
How to make 'right.m_onLeaveScope' accept the new empty function ?
The compiler complains:
"error C2275: 'default_empty<T>' : illegal use of this type as an expression"
due to 'm_onLeaveScope_empty(default_empty<T>)' on line
explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}
I can't see why i doesn't want to work.
"OnLeaveScopeFunc func = default_delete<T>"
Worked fine so why doesn't that want to work ?
I've also tried:
"OnLeaveScopeFunc func_empty = default_empty<T>"
'm_onLeaveScope_empty(func_empty)'
explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>, OnLeaveScopeFunc func_empty = default_empty<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(func_empty) {}
Thread Closed
This thread is kinda stale and has been closed but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.