Inspired by page 219 of Nicolai M. Josuttis's book, I set out to write a
class for an intrinsic array which would behave, to as far an extent as
possible, like a container. Also though, I wanted no overhead whatsoever.
The code I'm about to show below is not finished, it may contain the odd
oversight, bug or error (but at a quick glance it seems OK).
First of all though, I want to show you some macros I've written. The
purpose of the macros is to:
(1) Reduce repetitive typing.
(2) Reduce the possibility for error.
Before you use these macros though, you must a typedef to your class, e.g.:
class SmartPointerOrW hatever {
typedef SmartPointerOrW hatever ThisClass;
};
The first macro, "POST_IN_TERMS_ OF", expands to a definition of a post-
operator which invokes the corresponding pre-operator. Here it is:
#define POST_IN_TERMS_O F(op) \
ThisClass operator op(int) \
{ \
ThisClass const temp(*this); \
op *this; \
return temp; \
}
The second macro, "OP_IN_TERMS_OF ", expands to a definition of an operator
such as +, which works off +=. Here it is:
#define OP_IN_TERMS_OF( op,param_dec,ob j) \
ThisClass operator op(param_dec) const \
{ \
ThisClass temp(*this); \
temp op##= obj; \
return temp; \
}
(I realise that this won't work if "param_dec" were to contain a comma.)
The third macro, "NON_CONST_IN_T ERMS_OF", expands to a definition of a non-
const member function (or member function operator) which calls the const
version. Here it is:
#define NON_CONST_IN_TE RMS_OF(ret,func _dec,call) \
ret func_dec \
{ \
return \
const_cast<ret> ( \
const_cast<This Class const*>(this)-call \
); \
}
(I realise that this won't work if "func_dec" were to contain a comma.)
First of all I'd like to ask, what do you think of these macros? Has anyone
ever done something similar? You can see them in action now below as I
implement "IntrinsicArray ". Before that though, I'm going to implement a
reverse pointer for use as a reverse iterator:
#include <cstddef>
template<class T>
struct ReversePtr {
typedef ReversePtr ThisClass;
typedef std::size_t size_t;
T *p;
ReversePtr(T *const arg) : p(arg) {}
operator T*() { return p; }
operator T const *() const { return p; }
ReversePtr &operator++( ) { --p; return *this; }
ReversePtr &operator--() { ++p; return *this; }
POST_IN_TERMS_O F(++)
POST_IN_TERMS_O F(--)
ReversePtr &operator+=(siz e_t const i) { p -= i; return *this; }
ReversePtr &operator-=(size_t const i) { p += i; return *this; }
OP_IN_TERMS_OF( +,size_t const i,i)
OP_IN_TERMS_OF(-,size_t const i,i)
T const &operator[](size_t const i) const { return *(p-i); }
NON_CONST_IN_TE RMS_OF(T&,opera tor[](size_t const i),operator[](i))
};
What do you think of ReversePtr? I appreciate any suggestions.
Now here's IntrinsicArray:
#include <cstddef>
#include <stdexcept>
template<class T,std::size_t len>
struct IntrinsicArray {
typedef IntrinsicArray ThisClass;
T arr[len];
typedef T value_type;
typedef T *iterator;
typedef T const *const_iterator ;
typedef T &reference;
typedef T const &const_referenc e;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type ;
T const *begin() const { return arr; }
NON_CONST_IN_TE RMS_OF(T*,begin (),begin())
T const *end() const { return arr+len; }
NON_CONST_IN_TE RMS_OF(T*,end() ,end())
ReversePtr<T constrbegin() const { return arr+(len-1); }
NON_CONST_IN_TE RMS_OF(ReverseP tr<T>,rbegin(), rbegin())
ReversePtr<T constrend() const { return arr-1; }
NON_CONST_IN_TE RMS_OF(ReverseP tr<T>,rend(),re nd())
T const &operator[](size_type const i) const {return arr[i];}
NON_CONST_IN_TE RMS_OF(T&,opera tor[](size_type const i),operator[](i))
T const &at(size_typ e const i) const
{
if (i >= len) throw std::range_erro r();
return arr[i];
}
NON_CONST_IN_TE RMS_OF(T&,at(si ze_type const i),at(i))
T const &front() const { return *arr; }
NON_CONST_IN_TE RMS_OF(T&,front (),front())
T const &back() const { return arr[len-1]; }
NON_CONST_IN_TE RMS_OF(T&,back( ),back())
size_type size() const { return len; }
bool empty() const { return false; }
size_type capacity() const { return len; }
size_type max_size() const { return len; }
};
Of course, the code isn't finished yet, but I welcome any comments,
questions, or suggestions.
--
Frederick Gotham
Nov 22 '06
16 1861
Roland Pibinger wrote:
On Fri, 24 Nov 2006 05:57:07 -0500, Kai-Uwe Bux wrote:
>>You seem to think that there is a difference between:
typedef T* iterator; T* begin() { ... }
and:
typedef T* iterator; iterator begin() { ... }
But there is none.
Of course there is a difference! When a function returns T* one uses a
T* variable to capture the return value, when a function returns an
iterator an iterator variable is used.
Typedefs introduce aliases. Thus, a variable of type T* and a variable of
type IntrinsicArray< T>::iterator have the same type. The only difference is
in your head, not in the code. You are mistaking the implementation for the
documentation. The docs could (maybe should) state that the return type of
begin() is iterator. The implementation, however, is free to make up to
that promise in any way it pleases.
>>STL-style written client code will always use the IntrinsicArra y<T>::iterator typedef.
Nope. C/C++ clients can rely on the type of the return value because
that type is part of the function contract.
a) C++ does not support contracts in code. The contract is defined in the
documentation. We have not seen that. I already said that I would specify
iterator in the docs. However, one could, of course, make the guarantee
that iterator = T* part of the specification.
b) If you use the IntrinsicArray< Tas defined, then your code will not
break even if you use T*. Using iterator is still wise and makes your code
easier to change when you want to switch containers. But that's all.
Best
Kai-Uwe Bux
Kai-Uwe Bux wrote:
Roland Pibinger wrote:
On Fri, 24 Nov 2006 05:57:07 -0500, Kai-Uwe Bux wrote:
>You seem to think that there is a difference between:
typedef T* iterator;
T* begin() { ... }
and:
typedef T* iterator;
iterator begin() { ... }
But there is none.
Of course there is a difference! When a function returns T* one uses a
T* variable to capture the return value, when a function returns an
iterator an iterator variable is used.
Typedefs introduce aliases. Thus, a variable of type T* and a variable of
type IntrinsicArray< T>::iterator have the same type. The only difference is
in your head, not in the code.
Of course the difference is only in a human reader's head, but then
that's the only place that matters. There are always countless
different ways to express identical intent to the compiler and the
compiler will cope with all of them equally. Your job as a programmer
is to select, from all those possiblities, the one that most clearly
expresses your intent to a human being.
It is not clear from the first example whether begin() is intended to
return a T* because that is what iterator is typedef'd to be, or
whether begin() is intended to return a T* regardless of what type
iterator is.
In the second example, it is clear that begin() is meant to return an
iterator, whatever type that is.
You are mistaking the implementation for the
documentation. The docs could (maybe should) state that the return type of
begin() is iterator. The implementation, however, is free to make up to
that promise in any way it pleases.
I would argue that, at best, the at-first-glance mistmatch between the
documentation and your first code example needlessly introduces the
risk of confusing the reader for zero gain.
There is the other point that, as the maintainer of the class, if I
need to refactor the class to use a different type for iterator, or
maybe just a different type for a debug build, your first example makes
it harder for me to do that. Not very much harder necessarily,
depending how many function signatures use T* when they mean iterator,
but still needless extra complication for zero gain.
Gavin Deane
Roland Pibinger:
Of course there is a difference! When a function returns T* one uses a
T* variable to capture the return value, when a function returns an
iterator an iterator variable is used.
int Func1() { return 7; }
typedef int MyInt;
MyInt Func2() { return 7; }
int main()
{
int a = Func1();
MyInt b = Func1();
int c = Func2();
MyInt d = Func2();
a = b = c = d;
}
If that doesn't compile, burn your compiler.
--
Frederick Gotham
Gavin Deane:
Of course the difference is only in a human reader's head, but then
that's the only place that matters. There are always countless
different ways to express identical intent to the compiler and the
compiler will cope with all of them equally. Your job as a programmer
is to select, from all those possiblities, the one that most clearly
expresses your intent to a human being.
We will have to simply disagree on this.
I could have written "const_iterator " instead of "T const*", but I prefer the
latter as it's more tangible in my own mind. If you would prefer to write the
former, then fair enough that's your choice.
If I were to write documentation for "IntrinsicArray ", I might choose to list
the function signatures as returning "const_iterator " instead of "T const*".
--
Frederick Gotham
Gavin Deane wrote:
>
Kai-Uwe Bux wrote:
>Roland Pibinger wrote:
On Fri, 24 Nov 2006 05:57:07 -0500, Kai-Uwe Bux wrote: You seem to think that there is a difference between:
typedef T* iterator; T* begin() { ... }
and:
typedef T* iterator; iterator begin() { ... }
But there is none.
Of course there is a difference! When a function returns T* one uses a
T* variable to capture the return value, when a function returns an
iterator an iterator variable is used.
Typedefs introduce aliases. Thus, a variable of type T* and a variable of type IntrinsicArray< T>::iterator have the same type. The only difference is in your head, not in the code.
Of course the difference is only in a human reader's head, but then
that's the only place that matters. There are always countless
different ways to express identical intent to the compiler and the
compiler will cope with all of them equally. Your job as a programmer
is to select, from all those possiblities, the one that most clearly
expresses your intent to a human being.
The point is which human being we are talking about. Mr Pibinger, I took it,
was talking about a programmer of client code. Such programmer, in my
opinion, should not have any need to read the code for IntrinsicArray< >;
and there is no promise/contract/intent in the code conveyed to the client
programmer.
I grant you that the two variations of the code are not equivalent in
quality: the difference lies in ease of maintainance. There, you have a
point about conveying intent.
It is not clear from the first example whether begin() is intended to
return a T* because that is what iterator is typedef'd to be, or
whether begin() is intended to return a T* regardless of what type
iterator is.
In the second example, it is clear that begin() is meant to return an
iterator, whatever type that is.
The point where to make clear what begin() is supposed to return, is the
specs/docs for this class, not the code. If I open the header files for my
STL implementation of <vector>, I might find that begin() returns something
of type
whatever_fancy_ iterator_facade < value_type * >
where the standard says that begin() returns iterator. Should I believe the
standard or the header file?
>You are mistaking the implementation for the documentatio n. The docs could (maybe should) state that the return type of begin() is iterator. The implementation, however, is free to make up to that promise in any way it pleases.
I would argue that, at best, the at-first-glance mistmatch between the
documentation and your first code example needlessly introduces the
risk of confusing the reader for zero gain.
True.
There is the other point that, as the maintainer of the class, if I
need to refactor the class to use a different type for iterator, or
maybe just a different type for a debug build, your first example makes
it harder for me to do that. Not very much harder necessarily,
depending how many function signatures use T* when they mean iterator,
but still needless extra complication for zero gain.
Here, you are correct, too. I was not arguing that the two examples are
equal in code quality. I was arguing that they are equivalent in observable
behavior and make the same promisses to any client (namely none since, in
this case, promisses to clients have to be part of the docs/specs).
Best
Kai-Uwe Bux
Frederick Gotham wrote:
Gavin Deane:
Of course the difference is only in a human reader's head, but then
that's the only place that matters. There are always countless
different ways to express identical intent to the compiler and the
compiler will cope with all of them equally. Your job as a programmer
is to select, from all those possiblities, the one that most clearly
expresses your intent to a human being.
We will have to simply disagree on this.
As long as I never have to maintain code you wrote, that approach is
fine by me.
I could have written "const_iterator " instead of "T const*", but I prefer the
latter as it's more tangible in my own mind. If you would prefer to write the
former, then fair enough that's your choice.
The difference to me is that, if, conceptually, the function returns an
object that can be used to iterate through the sequence and can be
used, by dereferencing, to obtain an immutable object in that sequence,
then C++ has a name for such a concept and that name is const_iterator.
The fact that, at the particular point in time you wrote the code, the
underlying type you chose to model the const_iterator concept was a T
const* is incidental. It is an implementation detail. To confuse the
abstract concept with the implementation detail is indicative of a lack
of clarity of thinking.
If I were to write documentation for "IntrinsicArray ", I might choose to list
the function signatures as returning "const_iterator " instead of "T const*".
Then you still introduce the maintenance problem I described.
Gavin Deane
Kai-Uwe Bux wrote:
Gavin Deane wrote:
Kai-Uwe Bux wrote:
Roland Pibinger wrote:
On Fri, 24 Nov 2006 05:57:07 -0500, Kai-Uwe Bux wrote: You seem to think that there is a difference between:
typedef T* iterator;
T* begin() { ... }
and:
typedef T* iterator;
iterator begin() { ... }
But there is none.
<snip>
Of course the difference is only in a human reader's head, but then
that's the only place that matters. There are always countless
different ways to express identical intent to the compiler and the
compiler will cope with all of them equally. Your job as a programmer
is to select, from all those possiblities, the one that most clearly
expresses your intent to a human being.
The point is which human being we are talking about. Mr Pibinger, I took it,
was talking about a programmer of client code. Such programmer, in my
opinion, should not have any need to read the code for IntrinsicArray< >;
and there is no promise/contract/intent in the code conveyed to the client
programmer.
I was indeed talking about the human being who has to read the code. As
a client of std::vector, I have no more need to be able to comprehend
the code in my <vectorheader than my Grandmother does. I look in my
copy of the standard or my copy of Josuttis to find out what interface
std::vector provides. I don't look in the <vectorheader for that
information because I expect my standard library supplier to implement
the standard correctly or to documnet the instances where they don't
and as long as they do that, I don't care how they do it.
So let me rephrase:
Your job as a programmer is to select, from all those possiblities, the
one that most clearly expresses your intent to the human beings who
need to read your code.
I grant you that the two variations of the code are not equivalent in
quality: the difference lies in ease of maintainance. There, you have a
point about conveying intent.
It is not clear from the first example whether begin() is intended to
return a T* because that is what iterator is typedef'd to be, or
whether begin() is intended to return a T* regardless of what type
iterator is.
In the second example, it is clear that begin() is meant to return an
iterator, whatever type that is.
The point where to make clear what begin() is supposed to return, is the
specs/docs for this class, not the code. If I open the header files for my
STL implementation of <vector>, I might find that begin() returns something
of type
whatever_fancy_ iterator_facade < value_type * >
where the standard says that begin() returns iterator. Should I believe the
standard or the header file?
If you provide me with a class to use, I absolutely agree with you
that, to understand how to use that class I should look in the
documentation you provide, not in the code. If the code does not behave
as documented, the solution is to ask for a refund.
However, I was coming from the perspective of your successor on the
project. If you move on to bigger and brighter things and I pick up
your code and documentation to maintain, it is very important to me
(and therefore to my employer if we assume a professional context) that
your code and documentation are easy to comprehend.
<snip>
Gavin Deane This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics |
by: Jeremy Cowles |
last post by:
I have been reading a book that focuses on understanding the intrinsic types
of C++ in depth. The author's mentality is this: "Understand the intrinsic
types, then learn the std types as needed later", but I have been reading
the stroustrup (spelling?) book and he says that it is much better to learn
the standard library first as a beginner, and then worry about the intrinsic
types afterwards.
So there is no doubt that you need to have a...
|
by: justanotherguy63 |
last post by:
Hi,
I am designing an application where to preserve the hierachy and for
code substitability, I need to pass an array of derived class object in
place of an array of base class object. Since I am using vector
class(STL), the compiler does not allow me to do this.
I do realize there is a pitfall in this approach(size of arrays not
matching etc), but I wonder how to get around this problem. I have a
class hierachy with abstract base...
|
by: shmartonak |
last post by:
For maximum portability what should the type of an array index be? Can
any integer type be used safely? Or should I only use an unsigned type?
Or what?
If I'm using pointers to access array elements as *(mptr+k) where I've
declared
MYTYPE *mptr;
what should be the type of 'k'? Should it be ptrdiff_t?
|
by: Alek Davis |
last post by:
Hi,
Is it possible to access intrinsic ASP objects, such as Request, from a .NET
class. Say, I have a .NET library exposed via a COM or COM+ wrapper. Can
this library retrieve the request info (basically, server variables exposed
via the Request object), when it is invoked from a traditional ASP (not
ASP.NET) application? Any ideas? Thanks in advance.
Alek
|
by: bienwell |
last post by:
Hi all,
Data displayed on the datalist control is bound by the column name of the
dataset like this :
<%# DataBinder.Eval(Container.DataItem, "title")%>
Could I use an element of the array (i.e. index=0) which has the name
"title" in place of it ? For example:
| |
by: Paminu |
last post by:
If I have this struct:
#include <stdlib.h>
#include <stdio.h>
#define KIDS 4
typedef struct test {
int x;
int y;
} container;
|
by: toton |
last post by:
Hi,
In C++ when I initialize an array it, also initializes the class that
it contains, which calls the default constructor. However, I want to
initialize the array only (i.e reserve the space) and use my specific
constructor to initialize the class. How to do it without using malloc?
Something like Point* pt = new Point; I want it to reserve the
space for N points only, and not to call default constructor. I dont
have a default...
|
by: De_Kabal |
last post by:
I'm trying to bind a 12x16 array to a repeater to display the information in
a table to have certain format. Right now all it does is display all the
array elements on individual rows. Does anyone know how to display the data
on just the 12 row?
thanks;
Here is my code:
Binding:
|
by: Unite |
last post by:
I have a ArrayList which I add a Array to. How will I access the array from the ArrayList?
Example :
/*Storing the array*/
String Container = new String;
ArrayList arr =new ArrayList();
ResultSet rec1 = st.executeQuery("SELECT * FROM HistoryHeader WHERE DocumentDate BETWEEN '"+startDate+"' AND '"+endDate+"'");
while(rec1.next()) {
Container = rec1.getString("DocumentType");
|
by: Hystou |
last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it.
First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
|
by: Hystou |
last post by:
Overview:
Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
| |
by: tracyyun |
last post by:
Dear forum friends,
With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
|
by: isladogs |
last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM).
In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules.
He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms.
Adolph will...
|
by: conductexam |
last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one.
At the time of converting from word file to html my equations which are in the word document file was convert into image.
Globals.ThisAddIn.Application.ActiveDocument.Select();...
|
by: TSSRALBI |
last post by:
Hello
I'm a network technician in training and I need your help.
I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs.
The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols.
I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
|
by: adsilva |
last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
|
by: muto222 |
last post by:
How can i add a mobile payment intergratation into php mysql website.
| |
by: bsmnconsultancy |
last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...
| |