473,804 Members | 4,272 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Advanced topics... optimising / C++ / OOP / RefCounting / type-safety

Hi people,

I've been doing C for about 7 years now. But I'm new to C++.

I've decided that C++'s operator overloading could be very handy. I'm
writing something much like auto_ptr, except for my own set of classes.
Basically, it's a class that is supposed to be allocated automatically
on the stack, and possesess one member which is a pointer to an object.

Unlike auto_ptr, my object will actually call a lock or unlock function
upon the object. Each object has a "long RefCount" member, and a
"MyClass*" which is a pointer to some type information. The "class"
pointed to is just a statically allocated struct that contains some
nice info about each instance of itself, such as the size, the
constructor and destructor functions.

Basically I've written a ref-counting memory management system in C++.

I think anyone familiar with the STL string class shouldn't be too
worried about this approach, because most "string" implementations
actually use refcounts internally, anyhow.

And this is my first venture into properly using C++. So by default,
I'll be getting things wrong :o)

Well the code is correct. It compiles, and runs as I expect it to (took
some debugging but it's there now).

So what's my question?

Basically, is there a faster / smaller / neater way to do what I'm
doing? I've tested my code when setting the compiler to generate small
size code (for Mac PPC), and... well the refcounting system seems to
use A LOT of code just to do something very simple like:

Ref1 = Ref2;

Just that takes up about 52 bytes of in my compiled PPC executable!
Normally assigning a pointer takes 4 bytes, (that's 1 PPC instruction).

Well, obviously that's quite bad right?

The code executing is htis:

class OPObject {

OPStruct* Value;

OPObject& operator=(OPObj ect& obj) {
OPUnlock(Value) ;
Value = ref.Value;
OPLock(Value);
return *this;
}
This is actually an inline function. This is my first question, is it
even worth inlining this function?? Especially when I'm using two
inlined functions, OPLock and OPUnlock?

OPLock goes like this:

inline void OPLock( OPStruct* obj ) {
if (obj) {
obj->RefCount++;
}
}
inline void OPUnlock( OPStruct* obj ) {
if (obj) {
long rc = obj->RefCount - 1;
obj->RefCount = rc;
if (!rc) {
OPDeAlloc( obj );
}
}
}

Doesn't seem like much code, but it adds up...

It's a lot just to do "a = b;", really.

Of course, I don't necessarily need to use my OPObjects everywhere...
OPObject is just for memory management really. I can just extract the
OPStruct* and pass that around, knowing that it's a bare pointer that
could be invalid if stored outside of the lifetime of the OPObject.

Would I get faster code if I uninlined some stuff??

What about altering my code? Does anyone know a faster way to do
refcounting? Any OOP experts out there that know a faster way to do
this?? Any OOP experts out there that know how other refcounting
languages do their refcounting? Maybe if someone knows how Java or perl
or VisualBasic or other languages do it....??

The "if (obj)" thing irks me because I just wonder if there is a way to
do without it... but it seems like I must have it, because it's quite
possible to have an OPStruct* to 0, so I need to be able to handle
that. Unless someone can think of a clever way to avoid having to deal
with that? (One that doesn't involve me recoding my entire library
which currently relies on comparing object pointers to 0 to see if they
exist or not.)

Also, this brings me back to the question of neatness and simplicity.

I just don't know really how to make this into a compile-time type
checking safe system.

Right now I have a base class, OPStruct. All my other classes subclass
from this, so I have:

struct MySpecialClass : public OPStruct { /*...*/ };
struct MyOtherClass : public OPStruct { /*...*/ };

etc etc

Now, OPObject (with it's "OPStruct* Value"), doesn't actually specify
any type other than OPStruct.

OPObject has a handy converter to and from OPStruct*, so there is no
problem passing and returning the two types between each other.

So that means I can stuff a "MySpecialClass " into OPObject...

And then call this function MySpecialFuncti on(OPStruct* obj); like so

MySpecialFuncti on( MyOPObject ); // OPObject's operator convert to
OPStruct* is called.

because I've not specified any types in MySpecialFuncti on's params. if
I DID specify a type in MySpecialFuncti on's param, like so:

MySpecialFuncti on( MySpecialClass* obj );

then I can't call this:

MySpecialFuncti on( MyOPObject );

then I'll get a type error, because OPStruct* isn't necessarily a
MySpecialClass* !!

So what do I do?

I want compile-time type checking with a small simple way to manage
this. Can I do it with some special define trickery that lets me define
OPStruct* subclasses with their corresponding OPObject subclassing
owners?

Or is there a better way using templates? I don't want any overhead,
this must strictly be a compile-time type check remember.

Sorry for taking so long to explain myself but I think if I used less
works I wouldn't have stated the situation fully :o)

Thanks in advance to anyone smart enough and helpful enough to reply!

Nov 22 '05 #1
4 2097
st******@hotmai l.com wrote:
Hi people,

I've been doing C for about 7 years now. But I'm new to C++.

I've decided that C++'s operator overloading could be very handy. I'm
writing something much like auto_ptr, except for my own set of classes.
Basically, it's a class that is supposed to be allocated automatically
on the stack, and possesess one member which is a pointer to an object.
Implementing a smart pointer type is a fairly advanced project
for someone new to C++. It's true that std::auto_ptr is pretty
limited, but you might be better of using some other existing
implementation such as boost::shared_p tr rather than rolling
your own.

That said, I imagine you're really doing this as an exercise
and are having too much fun to stop. If so, it's still worth
looking at some existing implemetnations such as boost::shared_p tr
and boost::intrusiv e_ptr. The latter is pretty close to what
you're trying to do -- i.e., a reference-counting smart pointer
in which the ref count is stored as a member of the pointee
(hence "intrusive" ).
Unlike auto_ptr, my object will actually call a lock or unlock function
upon the object. Each object has a "long RefCount" member, and a
"MyClass*" which is a pointer to some type information. The "class"
pointed to is just a statically allocated struct that contains some
nice info about each instance of itself, such as the size, the
constructor and destructor functions.
It seems like you're manually reinvent stuff that C++ can do
for you. For example, if the exact type of the pointee is known
then just delete it using delete. If the actual type of the pointee
could be a derived type then you can still use delete as long as
you declare the base type with a virtual destructor. No need to
maintain your own function pointer to a destructor function --
C++ does that for you.
class OPObject {

OPStruct* Value;

OPObject& operator=(OPObj ect& obj) {
OPUnlock(Value) ;
Value = ref.Value;
OPLock(Value);
return *this;
}
The operand to operator= should generally be a const reference. It's
not in the case of auto_ptr but auto_ptr has really funky assignment
semantics.

Also, you should increment (lock) before you decrement (unlock) so you
don't mistakenly free the pointee in the case of self-assignment.

OPObject& operator=(OPObj ect const& obj)
{
OPLock(obj.Valu e);
OPUnlock(Value) ;
Value = obj.Value;
return *this;
}

I would suggest more descriptive names -- e.g., OPObject is a
pointer-like object that manages the lifetime of its pointee
using reference counting, so why not "CountedPtr " or something.
But I'll use your names here.
This is actually an inline function. This is my first question, is it
even worth inlining this function?? Especially when I'm using two
inlined functions, OPLock and OPUnlock?
You could try it both ways and see what the actual impact is on code
size. Be aware that inline is only a hint to the compiler anyway; it
may choose not to inline the function.
inline void OPLock( OPStruct* obj ) {
if (obj) {
obj->RefCount++;
}
}

inline void OPUnlock( OPStruct* obj ) {
if (obj) {
long rc = obj->RefCount - 1;
obj->RefCount = rc;
if (!rc) {
OPDeAlloc( obj );
}
}
}
These seem like part of the internal implementation of your smart
pointer class so make them private (and optionally static) member
functions if they aren't already. If they're member functions then
you don't need to add prefixes to the names -- just call them Lock
and Unlock.
Doesn't seem like much code, but it adds up...

It's a lot just to do "a = b;", really.

Of course, I don't necessarily need to use my OPObjects everywhere...
OPObject is just for memory management really. I can just extract the
OPStruct* and pass that around, knowing that it's a bare pointer that
could be invalid if stored outside of the lifetime of the OPObject.
Or invoke your smart pointer's operator* to obtain a reference and pass
that around. So whenever a function takes an OPStruct& parameter (or
OPStruct const&) it's clear that there's now change in "ownership"
implied, and it's also very cheap.

If you want to do something that actually imlples ownership, such as
adding the pointee to a container, then you'd typically pass the smart
pointer by const-reference.
The "if (obj)" thing irks me because I just wonder if there is a way to
do without it... but it seems like I must have it, because it's quite
possible to have an OPStruct* to 0...
Well, you own the smart pointer class so you can control its
invariants. That's what constructors and "private" are for. You
could design things such that your internal pointer is never NULL,
and represent the concept "pointer to nothing" by pointing to a
special instance, thus.

class OPObject
{
public:
// default-initialize to point to &NullValue
OPObject(OPStru ct* value = &NullValue);

private:
Value* Value;
static OPStruct NullValue;
};

Your Lock/Unlock would then no longer need null checks, but on the
other hand operator-> would probably need to do a check so as to
return null if Value==&NullVal ue, so whether it's a net win is up
to you to decide.
I just don't know really how to make this into a compile-time type
checking safe system.


As you guessed, the answer is templates. Again, you might want to look
at how boost::intrusiv e_ptr does this.

Nov 22 '05 #2
Fricken old people and their c stuff. Goodness.... You need to
upgrade EVERYTHING!!!!! !!!!!!!!!!!!!!! !!!!!!!!!!!!!!! !!!!!111

Nov 22 '05 #3
Hi Nik,

Thanks for the good answers. I've since corrected the lock order bug
you spotted in my code.

There are some more considerations that I should have said, so I'll say
them not.

My code is for a library that I hope to be usable from C and C++, hence
the tendancy to use structs instead of classes and using ordinary C
functions.

The C++ OPObject that did the refcounting automatically, is just a
syntactic sugar, not a functional requirement. I'd like it to work in
pure C. Not because I like C, but because it is so useful for library
development. A C API can be accessed from many languages or
environments that a C++ API might not.

By making the C++ automatic refcounting class be a superficial (but
very well crafted and incredibly handy) addition ontop of the C layer,
I'll allow for my compiled library to be executable from languages that
can access C APIs but not C++ APIs. Hence all the prefixes, like OPLock
instead of Lock.

Boost looks very useful actually. If I were using a pure C++
environment and not caring about general purpose library development,
I'd look into it. The shared_ptr and intrusive_ptr look pretty much
exactly what I'm doing...

I'd still be concerned about Boost's size. As it is I've managed to
implement a manual refcounting system that has constructors and
destructors, in just one small .cpp file (with an "Extern "C" API), and
an optional auto refcounting C++ class. Boost looks huge and scary :o)
My "ObjectPlatform " as I call it (hence the OP) is small and familiar.

I see that boost's shared_ptr is recommended for inclusion into the new
C++ standard, which makes sense as refcounting really is much harder to
cause errors such as leaks or dangling pointers, for the developer.

As for the templates for compile time type checking... well I've never
done templates before but I have no doubt I'll learn to like them if I
give it a shot. My only concern is size.

If I template a function that does exactly the same thing on classes of
different types, will I get different functions compiled? All I'm
looking for is type safety, remember. Same code, same run-time
requirements, different compile-time requirements.

One the one hand I expect this code to be compiled most often with
modern compilers like gcc. On the other hand even I still use a decade
old C++ compiler like Apple's old MrCpp. Why MrCpp? Because it creates
smaller faster code than CodeWarrior does. And CodeWarrior compiles
smaller faster code than gcc does. So MrCpp is the best in my
experience.

Basically I worry if enough compilers will be smart enough to use the
same code if templated.

But then it doesn't matter if I am inlining all my C++ functions
anyhow, right? As long as the OPLock and OPUnlock functions are
actually not inline, then I won't be getting too much code duplicated.

I can't really use a C++ class and use C++'s constructor/destructor,
because I need this library to be compatible across different OOP
execution environments. It will run in pure C++ environment, some
basics, and maybe even Java. So I need to have my own class system that
my ObjectPlatform can massage into a shape acceptable by the hosting
OOP environment.

The idea of using a private value which is never null... that seems
like a good idea. I'll try it out, see if the speed/size difference is
even noticable. Coders tend to worry about the wrong things. Small
details get blown out of proportion and big troubles pass under the
radar :o) I might have been worrying about nothing with the "if (obj)"
thing.

Thanks for all the good answers :o)

ni*****@microso ft.com wrote:
st******@hotmai l.com wrote:
Hi people,

I've been doing C for about 7 years now. But I'm new to C++.

I've decided that C++'s operator overloading could be very handy. I'm
writing something much like auto_ptr, except for my own set of classes.
Basically, it's a class that is supposed to be allocated automatically
on the stack, and possesess one member which is a pointer to an object.


Implementing a smart pointer type is a fairly advanced project
for someone new to C++. It's true that std::auto_ptr is pretty
limited, but you might be better of using some other existing
implementation such as boost::shared_p tr rather than rolling
your own.

That said, I imagine you're really doing this as an exercise
and are having too much fun to stop. If so, it's still worth
looking at some existing implemetnations such as boost::shared_p tr
and boost::intrusiv e_ptr. The latter is pretty close to what
you're trying to do -- i.e., a reference-counting smart pointer
in which the ref count is stored as a member of the pointee
(hence "intrusive" ).
Unlike auto_ptr, my object will actually call a lock or unlock function
upon the object. Each object has a "long RefCount" member, and a
"MyClass*" which is a pointer to some type information. The "class"
pointed to is just a statically allocated struct that contains some
nice info about each instance of itself, such as the size, the
constructor and destructor functions.


It seems like you're manually reinvent stuff that C++ can do
for you. For example, if the exact type of the pointee is known
then just delete it using delete. If the actual type of the pointee
could be a derived type then you can still use delete as long as
you declare the base type with a virtual destructor. No need to
maintain your own function pointer to a destructor function --
C++ does that for you.
class OPObject {

OPStruct* Value;

OPObject& operator=(OPObj ect& obj) {
OPUnlock(Value) ;
Value = ref.Value;
OPLock(Value);
return *this;
}


The operand to operator= should generally be a const reference. It's
not in the case of auto_ptr but auto_ptr has really funky assignment
semantics.

Also, you should increment (lock) before you decrement (unlock) so you
don't mistakenly free the pointee in the case of self-assignment.

OPObject& operator=(OPObj ect const& obj)
{
OPLock(obj.Valu e);
OPUnlock(Value) ;
Value = obj.Value;
return *this;
}

I would suggest more descriptive names -- e.g., OPObject is a
pointer-like object that manages the lifetime of its pointee
using reference counting, so why not "CountedPtr " or something.
But I'll use your names here.
This is actually an inline function. This is my first question, is it
even worth inlining this function?? Especially when I'm using two
inlined functions, OPLock and OPUnlock?


You could try it both ways and see what the actual impact is on code
size. Be aware that inline is only a hint to the compiler anyway; it
may choose not to inline the function.
inline void OPLock( OPStruct* obj ) {
if (obj) {
obj->RefCount++;
}
}

inline void OPUnlock( OPStruct* obj ) {
if (obj) {
long rc = obj->RefCount - 1;
obj->RefCount = rc;
if (!rc) {
OPDeAlloc( obj );
}
}
}


These seem like part of the internal implementation of your smart
pointer class so make them private (and optionally static) member
functions if they aren't already. If they're member functions then
you don't need to add prefixes to the names -- just call them Lock
and Unlock.
Doesn't seem like much code, but it adds up...

It's a lot just to do "a = b;", really.

Of course, I don't necessarily need to use my OPObjects everywhere...
OPObject is just for memory management really. I can just extract the
OPStruct* and pass that around, knowing that it's a bare pointer that
could be invalid if stored outside of the lifetime of the OPObject.


Or invoke your smart pointer's operator* to obtain a reference and pass
that around. So whenever a function takes an OPStruct& parameter (or
OPStruct const&) it's clear that there's now change in "ownership"
implied, and it's also very cheap.

If you want to do something that actually imlples ownership, such as
adding the pointee to a container, then you'd typically pass the smart
pointer by const-reference.
The "if (obj)" thing irks me because I just wonder if there is a way to
do without it... but it seems like I must have it, because it's quite
possible to have an OPStruct* to 0...


Well, you own the smart pointer class so you can control its
invariants. That's what constructors and "private" are for. You
could design things such that your internal pointer is never NULL,
and represent the concept "pointer to nothing" by pointing to a
special instance, thus.

class OPObject
{
public:
// default-initialize to point to &NullValue
OPObject(OPStru ct* value = &NullValue);

private:
Value* Value;
static OPStruct NullValue;
};

Your Lock/Unlock would then no longer need null checks, but on the
other hand operator-> would probably need to do a check so as to
return null if Value==&NullVal ue, so whether it's a net win is up
to you to decide.
I just don't know really how to make this into a compile-time type
checking safe system.


As you guessed, the answer is templates. Again, you might want to look
at how boost::intrusiv e_ptr does this.


Nov 22 '05 #4
On 2005-11-15, st******@hotmai l.com <st******@hotma il.com> wrote:
As for the templates for compile time type checking... well
I've never done templates before but I have no doubt I'll learn
to like them if I give it a shot. My only concern is size.

If I template a function that does exactly the same thing on
classes of different types, will I get different functions
compiled? All I'm looking for is type safety, remember. Same
code, same run-time requirements, different compile-time
requirements.

One the one hand I expect this code to be compiled most often
with modern compilers like gcc. On the other hand even I still
use a decade old C++ compiler like Apple's old MrCpp. Why
MrCpp? Because it creates smaller faster code than CodeWarrior
does. And CodeWarrior compiles smaller faster code than gcc
does. So MrCpp is the best in my experience.

Basically I worry if enough compilers will be smart enough to
use the same code if templated.


Check out Item 42 in _Effective C++_ (Meyers). There's a design
pattern there, whose name I don't know, that answers those
questions. You implement your interface generically once, to
operate on void*. Users use typesafe templates that inherit that
single generic implementation.

--
Neil Cerutti
Nov 22 '05 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

6
3198
by: Pedro Fonseca | last post by:
Greetings! Can someone please help me to crack this problem? I have 4 tables: Subject, Forum, Topic and Post. A Subject groups various Forums, a Forum groups various Topics, and a Topic groups several Posts. The references inside the tables are: +-----------+ +-----------+ +-----------+ +-----------+ | Subject | | Forum | | Topic | | Post | +-----------+ +-----------+ +-----------+ +-----------+
12
1981
by: joe martin | last post by:
In recent discussions relating to what to use for a new project which integrated the work of two, previously seperate, teams we got to the subject of our respective string implementations. One team rolled their own strings while the other used the std::string. Reasons for using the home-grown strings(and vectors) were mainly refcounting and portabillity, but I thought that these days almost all STL implementations used refcounted strings...
3
1285
by: M.Kumar | last post by:
Hi, We are looking for good technical writers on C++/VC++ topics. The materials can include tips/tutorials/advanced articles/project etc., Each materials can be paid upto a maximum of $15 depending on the type/size/quality of the contributions. If you feel you deserve more pls specify the reasons. If interested, please send an email to muthuis@lycos.com. Copyrighted materials or stuff that are copied from other websites/books are not...
9
2972
by: John Robert | last post by:
Hi! Anyone has recommendations for advanced ASP.NET books? By advanced, I mean complicated stuff... not just authentification and datagrid binding I'm mainly interrested in learning more about letting my customers customize a web forms (from a database... on the fly control creation and persisting data), and other advanced topics.
68
2667
by: pemo | last post by:
What would expect to be covered? Say you'd already attended a course, that had covered stuff like structs, pointers, bitwise and logical operators, multi-demensional arrays , *but* hadn't covered other stuff like recursion, unions, bit-fields . What topics would /should be covered on a course that takes students further?
22
2140
by: macAWM | last post by:
Hi list, First let me explain that my background is in Java and I am quite spoiled to its niceties (read "less ambiguous nature"). Anyway to my problems. 1. I want to write my own library for what I consider to be some holes in the standard language. How do I go about writing and compiling this without this stupid error about not having a 'main' function. I don't want a stupid 'main' function. (I'm compiling using gcc 4.0.) I would...
7
1677
by: ashu | last post by:
i m relatively new to this group. i just want to know, is there any site which can provide any help on advanced C topics such as machine interaction through C, memory management through C etc
3
1271
by: Jon Shemitz | last post by:
Advanced Delegates Delegates may be the least-understood part of the CLR. Delegates look a bit like method pointers, but the differences go well beyond delegates' multi-cast abilities. I will start with a quick survey of delegate basics, including invocation list editing and event syntax (which few people seem to really understand). I will then explore 2.0's anonymous methods before concluding with a lengthy discussion of asynchronous...
2
1809
by: Erich Pul | last post by:
hi! i'm sincerely in need for literature (in form of one or more books if possible) on advanced php topics like template systems, frameworks, MVC, ... can anyone provide a few titles or authors? many thanks in advance, erich
0
1181
by: learning | last post by:
this is a very godo C# course that teaches C# 3.0 features and some more advanced C#2.0 topics taught by SRobertson, the LADOTNET developer group president. -- Windows Forms Programming, rendering graphical data with GDI+, Windows Forms Control, and database access with ADO.NET. -- discusses web applications and XML web services, including ASP.NET Web Pages and Web Controls, ASP.NET web applications, and XML web services.
0
9710
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However, people are often confused as to whether an ONU can Work As a Router. In this blog post, we’ll explore What is ONU, What Is Router, ONU & Router’s main usage, and What is the difference between ONU and Router. Let’s take a closer look ! Part I. Meaning of...
0
9589
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,...
0
10593
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
0
10340
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10329
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,...
0
5527
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...
0
5663
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
4304
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
3
3000
bsmnconsultancy
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...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.