Suppose I have the following very simple class:
class A
{
public:
static constexpr A make() { return A{}; }
constexpr A() : _v(0) {}
constexpr A& setV(int v) { _v = v; return *this; }
private:
int _v;
};
If I try to use it as follows:
int main()
{
volatile A a1;
a1.setV(10);
//OR
//The optimizer will optimize this chain of constexpr calls into a single "store" instruction
a1 = A::make().setV(10); //More chaining here
return 0;
}
The code will not compile.
I understand why this is true based upon: Defining volatile class object
I know that the solution would be to add an additional method like so:
class A
{
public:
constexpr A() : _v(0) {}
volatile A& setV(int v) volatile { _v = v; return *this; }
constepxr A& setV(int v) { _v = v; return *this; }
private:
int _v;
};
As an aside, I am aware that returning the volatile reference will issue a warning if it is not used.
My question is, is there any way I can avoid code duplication in this case? Both implementations of setV will always be the same. Thus, if I add/change more methods, I have to maintain them both which could get messy if they aren't so trivial. The only difference is one of type qualifiers...
Since there have been some mentions of this in the comments, I thought I would note that I need volatile here because I am attempting to eventually implement "class overlay" for hardware peripheral register access on an embedded system.
In C++23, you can use an explicit object parameter (also known as deducing this) for this purpose:
class A
{
public:
A() : _v(0) {}
template <class Self>
constexpr auto&& setV(this Self&& self, int v) {
self._v = v; return self;
}
private:
int _v;
};
Unfortunately, as of this writing the only compiler that supports this is the latest version of Microsoft Visual C++.
I came up with one possible solution. It's dubious whether it's less verbose than what I had before, but at least I don't have to duplicate the body of the method. In fact, it's similar (in my mind) to the proposed C++23 answer by #ComicSansMS.
class A
{
public:
constexpr A() : _v(0) {}
template <typename T>
static constexpr void setV(T& dest, int src) { dest = src; }
volatile A& setV(int v) volatile { setV(_v, v); return *this; }
constepxr A& setV(int v) { setV(_v, v); return *this; }
private:
int _v;
};
T will be deduced as either int& or volatile int& depending on the context. Since its declare constexpr/inline, the compiler/optimizer still boils it down to a few assembly instructions.
I have these generic classes for 2D geometry:
template<class T> struct Point
{
T x,y;
//...Various member functions...
//T modulus() const noexcept { ... }
//T dist_from(const Point& other) const noexcept { ... }
//...
};
template<class T> class Polygon
{
public:
// ...An awful lot of member functions...
auto size() const noexcept { return vertexes.size(); }
//T area() const noexcept {...}
//T perimeter() const noexcept {...}
//T moment_of_inertia(const Point<T>&) const noexcept {...}
//void reorder_vertexes() {...}
//void transform(const Matrix& m) {...}
// ...
private:
std::vector<Point<T>> vertexes;
};
They work well and I use them in various part of the project. Now, for a particular application, I need Polygon but also need to associate some data to each of its vertexes.
Since Polygon vertexes can be transformed and reordered I would prefer to avoid the extra work of maintaining a parallel std::vector containing the additional data.
I would really like to introduce a new class like this:
template<class T, class D> class DecoratedPolygon
{
public:
struct DecoratedPoint
{
Point<T> point;
D data;
};
// Some specialized member functions...
const D& get_vertex_decoration(const std::size_t idx) const
{
return vertexes.at(idx).data;
}
// ...Then same member functions as polygon,
// except accessing the '.point'
auto size() const noexcept { return vertexes.size(); }
//...
private:
std::vector<DecoratedPoint> vertexes;
};
My problem whith this is that I don't want to rewrite a slightly modified version of all the member functions of Polygon.
How would you approach this particular case? I wonder if there is a zero cost technique to avoid that code duplication, or if simply I'm heading in the wrong way.
You could try to parameterize the Polygon class to take in the Point type as well:
template <class P> class Polygon {
std::vector<P> vertices
}
template <class T> class Point {
T x,y;
}
class DecoratedPoint : Point<long> {
int extraData;
}
Polygon<DecoratedPoint> newPoly;
The decorated data would end up coming from the point itself though, so the interface would look a bit different:
Polygon {
P get_point_at_idx( const std::size_t idx) {
return points.at(idx)
}
}
my_poly.get_point_at_idx(0).extraData
You'd need to change the implementation of Polygon a bit, but might provide more control longer term.
I have spent the last few days trying to create a generalised wrapper of sorts for function pointers in C++ and I've managed to solve nearly every single issue bar one. My main goal with this was to be able to simply call an object as a function with a function pointer stored internally. If the pointer was pointing somewhere then it would call it as normal while a null pointer would just not call the function and it would continue on as if nothing happened. I intend to use this primarily for callback function purposes, where I would likely not care if the function was called or not and just wanted to preform an action. It works almost perfectly with the following:
template<typename T>
class Action;
template<typename TReturn, typename ... TArgs>
class Action<TReturn(TArgs...)> {
public:
//! Define a type for the Action object
typedef TReturn(*signature)(TArgs...);
//! Constructors
inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {}
inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {}
//! Operator Call
inline bool operator() (TReturn& pReturn, TArgs ... pArgs) const { if (!mPointer) return false; pReturn = mPointer(pArgs...); return true; }
//! Operators
inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; }
inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; }
inline operator bool() const { return (mPointer != nullptr); }
private:
//! Store a pointer to the callback function
signature mPointer;
};
template<typename ... TArgs>
class Action<void(TArgs...)> {
public:
//! Define a type for the Action object
typedef void(*signature)(TArgs...);
//! Constructors
inline Action(const signature& pFunc = nullptr) : mPointer(pFunc) {}
inline Action(const Action& pCopy) : mPointer(pCopy.mPointer) {}
//! Operator Call
inline bool operator() (TArgs ... pArgs) const { if (!mPointer) return false; mPointer(pArgs...); return true; }
//! Operators
inline Action& operator=(const Action& pCopy) { mPointer = pCopy.mPointer; return *this; }
inline Action& operator=(const signature& pFunc) { mPointer = pFunc; return *this; }
inline operator bool() const { return (mPointer != nullptr); }
private:
//! Store a pointer to the callback function
signature mPointer;
};
However, I feel like the situation that would most likely use this wrapper is the output of debug information or formatted text. This could be through user defined functions or inbuilt functions such as printf. To match with printf's signature an Action would be created like:
Action<int(const char*, ...)> callback = printf;
and it would be able to operate the same way that any other Action would behave. The problem I'm finding is the '...' will force the template signature to not align with either of the specialisations, instead going with the first which is only a prototype.
I can fully understand why this doesn't work and why the compiler would not be able to handle the generation of the required class but I was hoping that someone here would know any sneaky ways to either achieve this or something similar. Any help would be much appreciated, thanks :)
The following example works for all function types, and lambdas with no capture:
#include <utility>
#include <cstdio>
#include <cmath>
template<typename Fn>
class Action {
Fn* function_ptr;
public:
Action() noexcept : function_ptr(nullptr) {}
Action(std::nullptr_t) noexcept : function_ptr(nullptr) {}
Action(const Action& other) : function_ptr(other.function_ptr) {}
Action(Fn f) : function_ptr(f) {}
Action& operator=(const Action& other) {
return (function_ptr = other.function_ptr, *this);
}
Action& operator=(std::nullptr_t ) {
return (function_ptr = nullptr, *this);
}
Action& operator=(Fn f) {
return (function_ptr = f, *this);
}
template<typename... Params>
auto operator()(Params&&... params) {
return function_ptr(std::forward<Params>(params)...);
}
};
Live Demo
As the comments under your question mention, using std::function is better than writing a wrapper for function pointers. The only problem with std::function is that it cannot be used with function signatures that contain ellipsis. If you explicitly specify the signature you can store e.g. printf as follows:
std::function<int(const char*, int, double, double)> fn = printf;
If you use C++17 or Boost, you can implement you own printf-like function, using std::any or Boost.Any which can be assigned to std::function as follows:
#include <iostream>
#include <string>
#include <vector>
#include <any>
#include <functional>
using namespace std;
void any_printf(string&& format, vector<any>&& args) {
int arg_index = 0; enum { NORMAL, CONVERT } mode;
for(auto& chr : format) {
if(mode == CONVERT) {
switch(chr) {
case 'd': cout << any_cast<int>(args[arg_index++]); break;
case 'f': cout << any_cast<float>(args[arg_index++]); break;
case 's': cout << any_cast<string>(args[arg_index++]); break;
/* ... */
default: cout << chr;
};
mode = NORMAL;
}
else {
chr == '%' ? (mode = CONVERT, 0) : (cout << chr, 0);
}
}
}
int main() {
using namespace string_literals;
function<void(string&&, vector<any>&&)> f_ptr { any_printf };
f_ptr("Cuboid: %smm x %dmm x %fmm.\n", { any("3"s), any(4), any(6.67f) });
return 0;
}
As an exercise for my personal enlightenment, I implement vector math with expression templates. I want to implement some operations that apply the same unary function to all elements to a vector expression. So far, I do this.
My base vector expression template is implemented like this
template <typename E>
class VectorExpr {
public:
int size() const { return static_cast<E const&>(*this).size(); }
float operator[](int i) const { return static_cast<E const&>(*this)[i]; }
operator E& () { return static_cast<E&>(*this); }
operator E const& () const { return static_cast<const E&>(*this); }
}; // class VectorExpr
Then, an object supposed to be a vector will look like this
class Vector2 : public VectorExpr<Vector2> {
public:
inline size_t size() const { return 2; }
template <typename E>
inline Vector2(VectorExpr<E> const& inExpr) {
E const& u = inExpr;
for(int i = 0; i < size(); ++i)
mTuple[i] = u[i];
}
private:
float mTuple[2];
};
Let's say I want to apply std::sin to all elements of an expression
template <typename E>
class VectorSin : public VectorExpr<VectorSin<E> > {
E const& mV;
public:
VectorSin(VectorExpr<E> const& inV) : mV(inV) {}
int size() const { return mV.size(); }
float operator [] (int i) const { return std::sin(mV[i]); }
};
Question => If I want to add more functions, I copy-paste what I do for the sin function, for every single function (like cos, sqrt, fabs, and so on). How I can avoid this kind of copy-pasting ? I tried things and figured out I'm still low in template-fu. No boost allowed ^^
template <typename F, typename E>
class VectorFunc : public VectorExpr<VectorFunc<F, E> > {
E const& mV;
public:
VectorSin(VectorExpr<E> const& inV) : mV(inV) {}
int size() const { return mV.size(); }
float operator [] (int i) const { return f(mV[i]); }
// this assumes the Functor f is default constructible, this is
// already not true for &std::sin. Adding the constructor that
// takes f, is left as an exercise ;)
F f;
};
In addition to the answer by pmr, The standard <cmath> functions aren't functors, so you couldn't use them directly to specify unique specialisations of your class - i.e. you wouldn't have a separate template instantiation for std::sin versus std::cos (which is what I gather you're aiming for? correct me if I've misunderstood you on that).
You could create a wrapper in order to map a function pointer to a distinct type, e.g.
#include <iostream>
template< void (*FuncPtr)() > struct Func2Type
{
void operator() () { FuncPtr(); }
};
void Hello() { std::cout << "Hello" << std::endl; }
void World() { std::cout << "world" << std::endl; }
int main()
{
Func2Type<Hello> test1;
Func2Type<World> test2;
test1();
test2();
}
That way you could use them as template arguments in the same way as a normal functor class
We use boost - so using that library should be fine.
But I've never managed to wrap my head around creating a set of templates which give you the right specialization for a whole class of data types, as opposed to specializing for a single data type (which I know how to do).
Let me go for an example to try to bring this down to earth. I want to have a set of classes which can be used as:
Initialized<T> t;
Where T is either a simple basic type, a PODS, or an array. It cannot be a class, for a class is expected to have its own constructor, and overwriting its raw memory is a terrible idea.
Initialized should basically memset(&t, 0, sizeof(t)); It makes it easier to ensure that runtime code is not different from debug code when dealing with legacy structs.
Initialized where SDT = simple data type, should simply create a struct which wrappers the underlying SDT and uses the compilers t() to generate the compiler defined default constructor for that type (it could amount to a memset as well, though it seems more elegant to simply result in t().
Here's a stab at it, using Initialized<> for PODs, and Initialised<> for SDTs:
// zeroed out PODS (not array)
// usage: Initialized<RECT> r;
template <typename T>
struct Initialized : public T
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_POD<T>::value));
// publish our underlying data type
typedef T DataType;
// default (initialized) ctor
Initialized() { Reset(); }
// reset
void Reset() { Zero((T&)(*this)); }
// auto-conversion ctor
template <typename OtherType> Initialized(const OtherType & t) : T(t) { }
// auto-conversion assignment
template <typename OtherType> Initialized<DataType> & operator = (const OtherType & t) { *this = t; }
};
And for SDTs:
// Initialised for simple data types - results in compiler generated default ctor
template <typename T>
struct Initialised
{
// default valued construction
Initialised() : m_value() { }
// implicit valued construction (auto-conversion)
template <typename U> Initialised(const U & rhs) : m_value(rhs) { }
// assignment
template <typename U> T & operator = (const U & rhs) { if ((void*)&m_value != (void*)&rhs) m_value = rhs; return *this; }
// implicit conversion to the underlying type
operator T & () { return m_value; }
operator const T & () const { return m_value; }
// the data
T m_value;
};
I have specialized Initialised for T*, to provide natural pointer behaviors. And I have an InitializedArray<> for arrays, which takes both the element type and array-size as template arguments. But again, I have to use template name to distinguish - I don't grok MPL well enough to provide a single template that results in the correct specialization at compile time all from a single name (Initialized<>, ideally).
I would love also to be able to provide an overloaded Initialized<typename T, T init_value> as well, so that for non-scalar values the user could define the default initialization value (or memset value)
I apologize for asking something that may take a bit of effort to answer. This seems to be a hurdle that I haven't been able to overcome in my own MPL reading on my own, but perhaps with some of your help I may be able to nail this functionality down!
Based on Uncle Ben's answer(s) below, I tried the following:
// containment implementation
template <typename T, bool bIsInheritable = false>
struct InitializedImpl
{
// publish our underlying data type
typedef T DataType;
// auto-zero construction
InitializedImpl() : m_value() { }
// auto-conversion constructor
template <typename U> InitializedImpl(const U & rhs) : m_value(rhs) { }
// auto-conversion assignment
template <typename U> T & operator = (const U & rhs) { if ((void*)&m_value != (void*)&rhs) m_value = rhs; return *this; }
// implicit conversion to the underlying type
operator T & () { return m_value; }
operator const T & () const { return m_value; }
// the data
T m_value;
};
// inheritance implementation
template <typename T>
struct InitializedImpl<T,true> : public T
{
// publish our underlying data type
typedef T DataType;
// auto-zero ctor
InitializedImpl() : T() { }
// auto-conversion ctor
template <typename OtherType> InitializedImpl(const OtherType & t) : T(t) { }
// auto-conversion assignment
template <typename OtherType> InitializedImpl<DataType> & operator = (const OtherType & t) { *this = t; }
};
// attempt to use type-traits to select the correct implementation for T
template <typename T>
struct Initialized : public InitializedImpl<T, boost::is_class<T>::value>
{
// publish our underlying data type
typedef T DataType;
};
And then tried a couple of usage tests.
int main()
{
Initialized<int> i;
ASSERT(i == 0);
i = 9; // <- ERROR
}
This results in an error: *binary '=' : no operator found which takes a right-hand operand of type 'InitializedImpl ' (or there is no acceptable conversion)
Whereas if I directly instantiate the correct base type (instead of a derived type):
int main()
{
InitializedImpl<int,false> i;
ASSERT(i == 0);
i = 9; // <- OK
}
Now I can use i as any old int. Which is what I want!
The exact same problems arise if I try to do the same for structs:
int main()
{
Initialized<RECT> r;
ASSERT(r.left == 0); // <- it does let me access r's members correctly! :)
RECT r1;
r = r1; // <- ERROR
InitializedImpl<RECT,true> r2;
r2 = r1; // OK
}
So, as you can see, I need some way to tell the compiler to promote an Initialized to act like a true T.
If C++ let me inherit from basic types, I could just use the inheritance technique and all would be well.
Or if I had some way to tell the compiler to extrapolate all methods in parent to child, so that anything valid on parent was valid on child, I'd be okay.
Or if I could use MPL or type-traits to typedef instead of inherit what I need, then there would be no child class, and no propagation issue.
Ideas?!...
Initialized should basically
memset(&t, 0, sizeof(t)); It makes it
easier to ensure that runtime code is
not different from debug code when
dealing with legacy structs.
I don't think you should need memset, because you can zero-initialize PODs just as you can explicitly invoke the default constructor of non-PODs. (unless I'm terribly mistaken).
#include <cassert>
struct X {int a, b; };
template <typename T>
struct Initialized
{
T t;
// default (initialized) ctor
Initialized(): t() { }
};
template <typename T>
struct WithInheritance: public T
{
// default (initialized) ctor
WithInheritance(): T() { }
};
int main()
{
Initialized<X> a;
assert(a.t.a == 0 && a.t.b == 0);
//it would probably be more reasonable not to support arrays,
//and use boost::array / std::tr1::array instead
Initialized<int[2]> b;
assert(b.t[0] == 0 && b.t[1] == 0);
WithInheritance<X> c;
assert(c.a == 0 && c.b == 0);
}
In your quest to determine the pod-ness of a type, you might also take into account this note from boost::is_pod reference:
Without some (as yet unspecified) help
from the compiler, is_pod will never
report that a class or struct is a
POD; this is always safe, if possibly
sub-optimal. Currently (May 2005) only
MWCW 9 and Visual C++ 8 have the
necessary compiler intrinsics.
(I think boost::type_traits are making it into the standard library in C++0x, and in such a case it would be reasonable to expect an is_pod that actually works.)
But if you want to specialize based on a condition, you can introduce a bool parameter. E.g something like this:
#include <limits>
#include <cstdio>
template <class T, bool b>
struct SignedUnsignedAux
{
void foo() const { puts("unsigned"); }
};
template <class T>
struct SignedUnsignedAux<T, true>
{
void foo() const { puts("signed"); }
};
//using a more reliable condition for an example
template <class T>
struct SignedUnsigned: SignedUnsignedAux<T, std::numeric_limits<T>::is_signed > {};
int main()
{
SignedUnsigned<int> i;
SignedUnsigned<unsigned char> uc;
i.foo();
uc.foo();
}
Here's also something that works sort of like you might be imagining (compiles at least with MinGW 4.4 and VC++ 2005 - the latter also nicely produces a warning that the array will be zero-initialized! :)).
This uses a default boolean argument which you probably shouldn't ever specify yourself.
#include <boost/type_traits.hpp>
#include <iostream>
template <class T, bool B = boost::is_scalar<T>::value>
struct Initialized
{
T value;
Initialized(const T& value = T()): value(value) {}
operator T& () { return value; }
operator const T& () const { return value; }
};
template <class T>
struct Initialized<T, false>: public T
{
Initialized(const T& value = T()): T(value) {}
};
template <class T, size_t N>
struct Initialized<T[N], false>
{
T array[N];
Initialized(): array() {}
operator T* () { return array; }
operator const T* () const { return array; }
};
//some code to exercise it
struct X
{
void foo() const { std::cout << "X::foo()" << '\n'; }
};
void print_array(const int* p, size_t size)
{
for (size_t i = 0; i != size; ++i) {
std::cout << p[i] << ' ';
}
std::cout << '\n';
}
template <class T>
void add_one(T* p, size_t size)
{
for (size_t i = 0; i != size; ++i) {
p[i] += T(1);
}
}
int main()
{
Initialized<int> a, b = 10;
a = b + 20;
std::cout << a << '\n';
Initialized<X> x;
x.foo();
Initialized<int[10]> arr /*= {1, 2, 3, 4, 5}*/; //but array initializer will be unavailable
arr[6] = 42;
add_one<int>(arr, 10); //but template type deduction fails
print_array(arr, 10);
}
However, Initialized will probably never be as good as the real thing. One short-coming is shown in the test code: it can interfere with template type deduction. Also, for arrays you'll have a choice: if you want to zero-initialize it with the constructor, then you can't have non-default array initialization.
If the usage is that you are going to track down all uninitialized variables and wrap them into Initialized, I'm not quite sure why you won't just initialized them yourself.
Also, for tracking down uninitialized variables, perhaps compiler warnings can help a lot.
I know it doesn't answer your question, but I thought POD structures were always zero-initialised anyway.
Since I was able to use UncleBen's answers to create a comprehensive solution (as good as I think it gets at this point in C++), I wanted to share it, below:
Feel free to use it, but I make no guarantees whatsoever about its worthiness for any use whatsoever, etc., etc., be an adult and take responsibility for your own damn actions, blah, blah:
//////////////////////////////////////////////////////////////
// Raw Memory Initialization Helpers
//
// Provides:
// Zero(x) where x is any type, and is completely overwritten by null bytes (0).
// Initialized<T> x; where T is any legacy type, and it is completely null'd before use.
//
// History:
//
// User UncleBen of stackoverflow.com and I tried to come up with
// an improved, integrated approach to Initialized<>
// http://stackoverflow.com/questions/2238197/how-do-i-specialize-a-templated-class-for-data-type-classification
//
// In the end, there are simply some limitations to using this
// approach, which makes it... limited.
//
// For the time being, I have integrated them as best I can
// However, it is best to simply use this feature
// for legacy structs and not much else.
//
// So I recommend stacked based usage for legacy structs in particular:
// Initialized<BITMAP> bm;
//
// And perhaps some very limited use legacy arrays:
// Initialized<TCHAR[MAX_PATH]> filename;
//
// But I would discourage their use for member variables:
// Initialized<size_t> m_cbLength;
// ...as this can defeat template type deduction for such types
// (its not a size_t, but an Initialized<size_t> - different types!)
//
//////////////////////////////////////////////////////////////
#pragma once
// boost
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
// zero the memory space of a given PODS or native array
template <typename T>
void Zero(T & object, int zero_value = 0)
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_POD<T>::value));
// make zeroing out a raw pointer illegal
BOOST_STATIC_ASSERT(!(boost::is_pointer<T>::value));
::memset(&object, zero_value, sizeof(object));
}
// version for simple arrays
template <typename T, size_t N>
void Zero(T (&object)[N], int zero_value = 0)
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_POD<T>::value));
::memset(&object, zero_value, sizeof(object));
}
// version for dynamically allocated memory
template <typename T>
void Zero(T * object, size_t size, int zero_value = 0)
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_POD<T>::value));
::memset(object, zero_value, size);
}
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Initialized for non-inheritable types
// usage: Initialized<int> i;
template <typename T, bool SCALAR = boost::is_scalar<T>::value>
struct Initialized
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_scalar<T>::value));
// the data
T m_value;
// default valued construction
Initialized() : m_value() { }
// implicit valued construction (auto-conversion)
template <typename U> Initialized(const U & rhs) : m_value(rhs) { }
// assignment
template <typename U> T & operator = (const U & rhs) { if ((void*)&m_value != (void*)&rhs) m_value = rhs; return *this; }
// implicit conversion to the underlying type
operator T & () { return m_value; }
operator const T & () const { return m_value; }
// zero method for this type
void _zero() { m_value = T(); }
};
//////////////////////////////////////////////////////////////////////////
// Initialized for inheritable types (e.g. structs)
// usage: Initialized<RECT> r;
template <typename T>
struct Initialized<T, false> : public T
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_POD<T>::value));
// default ctor
Initialized() : T() { }
// auto-conversion ctor
template <typename OtherType> Initialized(const OtherType & value) : T(value) { }
// auto-conversion assignment
template <typename OtherType> Initialized & operator = (const OtherType & value) { *this = value; }
// zero method for this type
void _zero() { Zero((T&)(*this)); }
};
//////////////////////////////////////////////////////////////////////////
// Initialized arrays of simple types
// usage: Initialized<char, MAXFILENAME> szFilename;
template <typename T, size_t N>
struct Initialized<T[N],false>
{
// ensure that we aren't trying to overwrite a non-trivial class
BOOST_STATIC_ASSERT((boost::is_POD<T>::value));
// internal data
T m_array[N];
// default ctor
//Initialized() : m_array() { } // Generates a warning about new behavior. Its okay, but might as well not produce a warning.
Initialized() { Zero(m_array); }
// array access
operator T * () { return m_array; }
operator const T * () const { return m_array; }
// NOTE: All of the following techniques leads to ambiguity.
// Sadly, allowing the type to convert to ArrayType&, which IMO should
// make it fully "the same as it was without this wrapper" instead causes
// massive confusion for the compiler (it doesn't understand IA + offset, IA[offset], etc.)
// So in the end, the only thing that truly gives the most bang for the buck is T * conversion.
// This means that we cannot really use this for <char> very well, but that's a fairly small loss
// (there are lots of ways of handling character strings already)
// // automatic conversions
// operator ArrayType& () { return m_array; }
// operator const ArrayType& () const { return m_array; }
//
// T * operator + (long offset) { return m_array + offset; }
// const T * operator + (long offset) const { return m_array + offset; }
//
// T & operator [] (long offset) { return m_array[offset]; }
// const T & operator [] (long offset) const { return m_array[offset]; }
// metadata
size_t GetCapacity() const { return N; }
// zero method for this type
void _zero() { Zero(m_array); }
};
//////////////////////////////////////////////////////////////////////////
// Initialized for pointers to simple types
// usage: Initialized<char*> p;
// Please use a real smart pointer (such as std::auto_ptr or boost::shared_ptr)
// instead of this template whenever possible. This is really a stop-gap for legacy
// code, not a comprehensive solution.
template <typename T>
struct Initialized<T*, true>
{
// the pointer
T * m_pointer;
// default valued construction
Initialized() : m_pointer(NULL) { }
// valued construction (auto-conversion)
template <typename U> Initialized(const U * rhs) : m_pointer(rhs) { }
// assignment
template <typename U> T * & operator = (U * rhs) { if (m_pointer != rhs) m_pointer = rhs; return *this; }
template <typename U> T * & operator = (const U * rhs) { if (m_pointer != rhs) m_pointer = rhs; return *this; }
// implicit conversion to underlying type
operator T * & () { return m_pointer; }
operator const T * & () const { return m_pointer; }
// pointer semantics
const T * operator -> () const { return m_pointer; }
T * operator -> () { return m_pointer; }
const T & operator * () const { return *m_pointer; }
T & operator * () { return *m_pointer; }
// allow null assignment
private:
class Dummy {};
public:
// amazingly, this appears to work. The compiler finds that Initialized<T*> p = NULL to match the following definition
T * & operator = (Dummy * value) { m_pointer = NULL; ASSERT(value == NULL); return *this; }
// zero method for this type
void _zero() { m_pointer = NULL; }
};
//////////////////////////////////////////////////////////////////////////
// Uninitialized<T> requires that you explicitly initialize it when you delcare it (or in the owner object's ctor)
// it has no default ctor - so you *must* supply an initial value.
template <typename T>
struct Uninitialized
{
// valued initialization
Uninitialized(T initial_value) : m_value(initial_value) { }
// valued initialization from convertible types
template <typename U> Uninitialized(const U & initial_value) : m_value(initial_value) { }
// assignment
template <typename U> T & operator = (const U & rhs) { if (&m_value != &rhs) m_value = rhs; return *this; }
// implicit conversion to underlying type
operator T & () { return m_value; }
operator const T & () const { return m_value; }
// the data
T m_value;
};
//////////////////////////////////////////////////////////////////////////
// Zero() overload for Initialized<>
//////////////////////////////////////////////////////////////////////////
// version for Initialized<T>
template <typename T, bool B>
void Zero(Initialized<T,B> & object)
{
object._zero();
}