Good idea or gimmick: Go-style OO-programming in C++ ?

Ö

Öö Tiib

char* CstringGive ()

Error! I see 'char*' my rules suggested against raw pointers. ;)
{
::cmw::marshalling_integer slen(*this);
::std::unique_ptr<char[]> cstr:):new char[slen.value + 1]);

You should not use smart pointers for dynamic arrays. Use std::vector
or std::string. Smart pointer does 'delete', not 'delete[]' and so
you result with undefined behavior.

This page --
http://en.cppreference.com/w/cpp/memory/unique_ptr
-- says,
"There are two versions of std::unique_ptr:

1) Manages the lifetime of a single object (e.g. allocated with new)
2) Manages the lifetime of a dynamically-allocated array of objects (e.g.allocated with new[])"

Ok, I stand corrected. Still I suggested to use std::vector
(or std::string) instead. Those have some immediately useful additional
data and member functions.
You also said,
"Never dereference a pointer without comparing it with
'nullptr' first."

Do you object to this line:

::cmw::marshalling_integer slen(*this);

?

No. 'this' is not pointer variable. It is keyword that resolves into
rvalue pointer that may never be 'nullptr'. It is not a reference
only because it was added into language before references.
See http://www.stroustrup.com/bs_faq2.html#this

When 'this' somehow is 'nullptr' then some undefined behavior is
already in effect.
 
W

woodbrian77

On Monday, 11 March 2013 16:23:56 UTC+2, (e-mail address removed) wrote:

Ok, I stand corrected. Still I suggested to use std::vector

(or std::string) instead. Those have some immediately useful additional

data and member functions.

I'm fine with vector and string for the most part.

I have another function called charStarGive that
also returns a char*. The sending side has to
embed a null into the stream in order for things
to work correctly. Here's one place I use it:

if(!cmwBuf.boolGive()){
throw failure:):std::string("Login failed: ").append(cmwBuf.charStarGive()));
}

If I change that to
... append(cmwBuf.stringGive()) ...

The executable is 120 bytes bigger. I don't know
which way is better since the way I'm doing it
requires an extra byte in the message.

Brian
http://webEbenezer.net
 
S

Seungbeom Kim

I have never needed to do anything like that. My rule of thumb has
worked just fine.

I guess that the first example ("std::unique_ptr<X> px(new X(...));"),
at least, is the most typical way to use std::unique_ptr. If you've
never given a new expression to the constructor of std::unique_ptr,
I wonder what else you've been giving.
 
I

Ian Collins

I'm fine with vector and string for the most part.

I have another function called charStarGive that
also returns a char*. The sending side has to
embed a null into the stream in order for things
to work correctly. Here's one place I use it:

if(!cmwBuf.boolGive()){

boolGive seems to be a strange name, shouldn't the member name have
meaning (like "isOK") rather than a type?
throw failure:):std::string("Login failed: ").append(cmwBuf.charStarGive()));

Why do you qualify std?
}

If I change that to
... append(cmwBuf.stringGive()) ...

The executable is 120 bytes bigger. I don't know
which way is better since the way I'm doing it
requires an extra byte in the message.

Do you work in a small embedded environment?
 
W

woodbrian77

boolGive seems to be a strange name, shouldn't the member name have

meaning (like "isOK") rather than a type?

That function is basically de-serializing a bool from a
buffer. That class also has a function named stringGive.
I'm all for meaningful names, but don't know if isOK would
be better.
Why do you qualify std?

To avoid surprises. That particular use is in
an executable so am less concerned about that
than if it were in a library. But someone could
do something like this in their code:

namespace abcdefg {
namespace std {


}
}

The above might be written by an incompetent
programmer or maybe it's done as an intentional
hack. However it happens, it's likely to cause
confusion. So I write it that way so there's no
ambiguity if the environment is somehow corrupted.

Do you work in a small embedded environment?

Not according to my literal understanding of those
terms, but in a way yes.

I don't have a big budget to accomplish my task.
My task is to provide a quality code generation
service to the world from (at this time) one
location.

I've taken thousands of steps over the course of
years to make the code as efficient as I know how.
I've received a lot of help from people here and
on other newsgroups to this end. Not having to
pay for what I don't need is important to the work.
People are free to examine the code on my website
to decide for themselves how the work is going.


Brian
Ebenezer Enterprises - in G-d we trust.
http://webEbenezer.net
 
Ö

Öö Tiib

I'm fine with vector and string for the most part.

I have another function called charStarGive that
also returns a char*. The sending side has to
embed a null into the stream in order for things
to work correctly. Here's one place I use it:

if(!cmwBuf.boolGive()){

Typically names like "isOk", "good" or "fine" are used for clarity.

Note that 'throw' is expensive keyword; one should not write code
that does throw hundreds of times per second. It is for exceptional
situations. Therefore put most weight into clarity and usefulness of
information that you throw and care less about efficiency of gathering
it.

failure:):std::string("Login failed: ").append(cmwBuf.charStarGive()));
}

If I change that to
... append(cmwBuf.stringGive()) ...

The executable is 120 bytes bigger.

I always pick clarity of code:

failure("Login failed: " + cmwBuf.message());

Later I find places modifying what I can raise the overall speed of
application two times and other places where I can reduce its total
memory usage two times. It is easier to do it without breaking something
if the code and data structure are clear and robust.
I don't know which way is better since the way I'm doing it requires an
extra byte in the message.

Extra byte in message is important when the messages consume significant
part of overall memory consumption. In every application there are only
few things that do it. Those are the things worth *major* attention. Rest
do not matter.

When we target platform that has only few kilobytes of memory then every
byte is important but then we do not throw exceptions with fancy dynamic
strings attached. We return int on such platforms 'return LoginFailed;'
 
Ö

Öö Tiib

Is this still true?
(A quick test on my machine shows I can do 10,000,000 throw-catch cycles
in 23 seconds)

Good catch, Andy, confirmed!

My error: I have done my tests with bloody MS debugger attached. That
evil thing spammed nonsense like "First-chance exception at 0x7c812fd3
(kernel32.dll) in Trying.exe: Microsoft C++ exception: bool at memory
location 0x0012ff6b" into some trace channel of IDE and killed the
performance thousand times despite it was release version. :-(

throw is still 300 times slower than just returning a value but not
300 000 times worse like I thought. Thanks!
 
W

woodbrian77

I always pick clarity of code:



failure("Login failed: " + cmwBuf.message());

I tried this:

throw failure("Login failed: ") << cmwBuf.charStarGive();

but it was 200 bytes larger than the original:

throw failure:):std::string("Login failed: ").append(cmwBuf.charStarGive()));

.. I value clarity and brevity, but the longer form
may be needed in this case.

The following has to do with your cmwBuf.message() code.
The message, from my perspective, consists of a bool and
an optional string depending on the value of the bool.

Here's the original snippet:

if(!cmwBuf.boolGive()){
throw failure:):std::string("Login failed: ").append(cmwBuf.charStarGive()));
}

Later I find places modifying what I can raise the overall speed of

application two times and other places where I can reduce its total

memory usage two times. It is easier to do it without breaking something

if the code and data structure are clear and robust.

Here's a link to the program -
http://webEbenezer.net/misc/cmwAmbassador.cc
.. I think it's decent/clear code, but admit there
are areas that need work.

Brian
Ebenezer Enterprises - helping to rebuild America.
http://webEbenezer.net
 
R

Rui Maciel

Andy said:
Is this still true?

(A quick test on my machine shows I can do 10,000,000 throw-catch cycles
in 23 seconds)

Arguing against exception handling on the basis of an assumed performance
penalty is something which is only done if the point of using exceptions was
entirely missed. So, the answer to that question should be: who cares?


Rui Maciel
 
Ö

Öö Tiib

Here's the original snippet:

if(!cmwBuf.boolGive()){
throw failure:):std::string("Login failed: ").append(cmwBuf.charStarGive()));
}

That charStarGive() looks dangerous because it provides non-const char
pointer into that private 'wrapper' member vector of your ReceiveBuffer.
What I meant was:

if(!cmwBuf.boolGive()){
throw failure("Login failed: " + cmwBuf.stringGive());
}

Other things that I randomly noticed:

The boolGive() that I saw was such:

bool boolGive () {
unsigned char boolRep;
Give(&boolRep, sizeof(boolRep));
return !!boolRep;
}

Inefficiency here matters if you transport lots of bools. Why not:

bool boolGive () {
return GiveOne() != 0;
}

It is likely more clear and robust too.
 
W

woodbrian77

That charStarGive() looks dangerous because it provides non-const char

pointer into that private 'wrapper' member vector of your ReceiveBuffer.

Good point. I added a const to the signature.
What I meant was:



if(!cmwBuf.boolGive()){

throw failure("Login failed: " + cmwBuf.stringGive());

}



Other things that I randomly noticed:



The boolGive() that I saw was such:



bool boolGive () {

unsigned char boolRep;

Give(&boolRep, sizeof(boolRep));

return !!boolRep;

}



Inefficiency here matters if you transport lots of bools. Why not:



bool boolGive () {

return GiveOne() != 0;

}



It is likely more clear and robust too.

Thank you. I've changed it now.

Brian
Ebenezer Enterprises
 
W

woodbrian77

I will report one thing.



#if 0

char* CstringGive ()

{

::cmw::marshalling_integer slen(*this);

char* cstr = ::new char[slen.value + 1];



try {

Give(cstr, slen.value);

*(cstr + slen.value) = '\0';

} catch (...) {

::delete [] cstr;

throw;

}

return cstr;

}

#else

char* CstringGive ()

{

::cmw::marshalling_integer slen(*this);

::std::unique_ptr<char[]> cstr:):new char[slen.value + 1]);



Give(cstr.get(), slen.value);

*(cstr.get() + slen.value) = '\0';

return cstr.get();

}

#endif



An executable produced by g++ 4.7.2 with -Os is 32 bytes

larger using the second version than the first. Maybe it's

a weakness of this compiler? :)

I tried this with Clang and -O3 (I decided to use
-O3 after thinking about how the performance tests
I have use -O3. I'm still a little partial to -O2
as we have discussed previously, but for now am
using -O3.) and got the opposite result as I found
with g++. Clang produced a 32 byte smaller executable
with the version that uses unique_ptr. I'm going to
switch to the version that uses unique_ptr.
 
8

88888 Dihedral

Öö Tiib? 2013?3?1????UTC+8??3?12?35????Lets face the real problems of C++ programming:

The function identity is resolved by the name and parameter types.

The template part allows different types of variables
or parameters to be mixed in the source codes in
declaring functions with the same name.

Thus, it is not as easy to debug as other languages
without some tools.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top