Wednesday, November 23, 2016

Digest for comp.lang.c++@googlegroups.com - 25 updates in 7 topics

Juha Nieminen <nospam@thanks.invalid>: Nov 23 07:06AM

> function extern I should have thought it would have no effect at all,
> since functions don't have storage - they just have linkage/visibility.
> So the extern declaration seems redundant.
 
What I meant was that in one compilation unit you have like:
 
void foo();
int main() { foo(); }
 
and then in another compilation unit you have:
 
inline void foo() { std::cout << "hello"; }
 
At least in the past, with some version of gcc, this may have produced
a linker error.
ruben safir <ruben@mrbrklyn.com>: Nov 23 05:51AM -0500

On 11/22/2016 06:12 PM, Chris Vine wrote:
> I'm not really sure about that one. In C, inline functions have
> internal linkage by default, which seems logical. In C++ they have
> external linkage by default, which on the face of it seems illogical
 
 
why?
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 23 11:30AM

On Wed, 23 Nov 2016 07:06:57 +0000 (UTC)
 
> inline void foo() { std::cout << "hello"; }
 
> At least in the past, with some version of gcc, this may have produced
> a linker error.
 
Ah right, I see what you mean. With breaches of the One Definition Rule
of this kind, the result you mention seems highly plausible, perhaps
even likely.
 
Chris
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 23 11:38AM

On Wed, 23 Nov 2016 05:51:56 -0500
> > external linkage by default, which on the face of it seems
> > illogical
 
> why?
 
Every ODR-user of an inline function has to see the definition. So what
then is the point of giving it external linkage in C++? (I don't know
the answer to that question, it is not rhetorical.)
"Öö Tiib" <ootiib@hot.ee>: Nov 23 04:50AM -0800

On Wednesday, 23 November 2016 13:38:35 UTC+2, Chris Vine wrote:
 
> Every ODR-user of an inline function has to see the definition. So what
> then is the point of giving it external linkage in C++? (I don't know
> the answer to that question, it is not rhetorical.)
 
May be it is for to make sure that the function-local static variables
in inline function are same for all instances. C++ compilers aggressively
ignore that suggestion given by 'inline' keyword to inline the function.
Therefore the 'inline' keyword basically means "function with external
linkage whose body may be is defined in header that may be is
included in several compilation units without causing ODR violations".
Tim Rentsch <txr@alumni.caltech.edu>: Nov 23 08:04AM -0800


> In C, inline functions have internal linkage by default, [...].
 
I believe this is not exactly right. As I read the C standard, a
function definition that has 'inline' but does not have 'extern'
still has external linkage, but does not provide an external
definition. Any call to said function might call the inline one
or it might call the externally defined one. (Who thought _that_
rule up? Sounds like a recipe for disaster.) So inline in C is
sort of like internal linkage, and sort of not.
 
> In C++ they have external linkage by default, [...].
 
AFAICT C++ is the same as C as far as linkage is concerned (ie,
for inline functions), but is more strict about how inline
functions may be defined in other translation units. Also, C++
allows more latitude for use of 'static' variable definitions
in inline functions.
 
Disclaimer: all of the foregoing is right to the best of my
understanding, but especially in the case of C++ there may be
something I missed or overlooked.
Juha Nieminen <nospam@thanks.invalid>: Nov 21 07:52AM

> think it is saying that a static has to be declared and defined within a
> compilational unit. I'm not sure why, though. And I've asked before,
> and read, but I'm still not clear on what a compilation unit is.
 
A static member variable needs to exist somewhere in the executable
(because, among other things, you need to be able to eg. have a pointer
pointing to it). You have to tell the compiler which compilation unit
(in practice which object file) the actual instance of that variable is.
The compiler won't guess; you need to tell it explicitly where it should
be put.
 
(Incidentally, C++17 will introduce the concept of inline variables,
which will allow you to define static member variables in the header
file itself, causing the linker to merge all the instantiations into
one. But that will be available only after compilers start supporting
it.)
ruben safir <ruben@mrbrklyn.com>: Nov 21 04:55AM -0500

On 11/21/2016 03:10 AM, Paavo Helde wrote:
 
thank you. You've clear up what I find is a subtle point which can
cause great bugs. I appreciate you taking the time to discuss this with me.
 
Reuvain
Lino <lino@net.com>: Nov 23 06:52PM +0100

Script Below
 
 
--------------
AB = CreateObject("Broker.Application");
sts = AB.Stocks();
Qty = sts.Count;
 
 
for( i = Qty - 1; i >= 0; i = i - 1 )
{
st = sts.Item( i );
Ticker = st.Ticker;
printf("changing " + ticker + "\n" );
Length = StrLen(Ticker );
if( StrFind(ticker, ".TO") )
st.Ticker = StrLeft( ticker, Length-3)+"-TC";
}
 
-------------
 
I Have a database with many symbols and the script [above] change latest
(RIGHT) 3 letter of any symbol.
 
Now, how can I change the script above to modify the "FIRST THREE" of any
Symbol ?
 
 
Thanks
Lino
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 23 01:10PM -0700

>(RIGHT) 3 letter of any symbol.
 
>Now, how can I change the script above to modify the "FIRST THREE" of any
>Symbol ?
 
This might help:
 
https://www.amibroker.com/guide/afl/strright.html
 
Louis
Lino <lino@net.com>: Nov 23 09:19PM +0100

Louis Krupp scriveva il 23/11/2016 :
 
> This might help:
 
> https://www.amibroker.com/guide/afl/strright.html
 
> Louis
 
 
oh yes, I try to change st.Ticker
 
st.Ticker= "TC-"+StrRight(ticker,Lenght-3);
 
but won't work. Can You help me?
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 23 01:35PM -0700


>oh yes, I try to change st.Ticker
 
> st.Ticker= "TC-"+StrRight(ticker,Lenght-3);
 
>but won't work. Can You help me?
 
When you say it doesn't work, what do you mean? What happens when you
try it? What does st.Ticker look like before and after you try to
change it?
 
Louis
Lino <lino@net.com>: Nov 23 09:37PM +0100

Louis Krupp ha detto questo mercoledì :
 
> This might help:
 
> https://www.amibroker.com/guide/afl/strright.html
 
> Louis
 
this work well
 
st.Ticker= "IT_"+StrRight(ticker,Lenght+3);
 
 
but change only 1 symbol (the last) and I have hundred symbols.
Maybe the problem iis in the FOR cycle.
Can you help me?
 
Thanks.
Lino
bitrex <bitrex@de.lete.earthlink.net>: Nov 23 01:02PM -0500

I'm working on a little graphics engine using the Allegro 5 library:
 
http://liballeg.org/
 
I'm writing it in C++, but the library calls are all C.
 
I have my demo working OK at the moment, but what I'd really like to
have is two threads: one taking care of handling the display rendering,
and one to run the script on the underlying objects and update their
positions, effects, etc.
 
Towards this goal right now the "visualizer" class which contains the
main display loop has a member std::map which holds shared_ptrs of an
"AbstractDisplayObject" type/interface, which defines a few pure virtual
functions for all the stuff you can do with a display object: update its
position, change its projection transform, render it to the screen, etc.
 
It's then subclassed by say "DisplayObject2D" for a 2D sprite where the
methods are overloaded appropriately and a unique_ptr to the texture
resource is created on instantiation. Adding another object to the
display is then just stuffing a new shared_ptr instance from the class
which composites say a "DisplayObject2D" into the map, and then
iterating over it each frame.
 
When a display object is removed to the script it passes a message into
the event queue telling the visualizer to dump its shared_ptr from the
map by ID-lookup before the object holding the first shared_ptr to the
object goes bye-bye.
 
So Allegro also has a C threading library which seems to be a layer on
top of POSIX threads:
 
https://www.allegro.cc/manual/5/allegro_thread
 
It has mutexs and conditions. Clearly I don't want the visualizer
pulling vector coordinates from a display object reference while the
other thread is writing to them.
 
First question: since I'm using C++11, is there a good reason to use the
C threading implementation provided by the library over std::thread?
 
Second question: if it's the former, what's the best way to wrap up that
API in a class where I can pass in another class instance, say
containing an infinite loop public method for the thread job, such that
the resource locking on the display objects is handled automatically?
The C threading API requires all thread functions passed as callbacks to
the lib to adhere to the following prototype:
 
static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg)
 
Where the latter argument is used to pass in a reference to some fashion
of structure containing the conditions, mutexes, resources to be locked,
etc.
Tim Rentsch <txr@alumni.caltech.edu>: Nov 23 08:22AM -0800


> Well, yes. But it'd be nice if those two thing were functionally the
> same. (At least for values of .length() that commonly occur in programs,
> i.e. less than SIZE_MAX / 2)
 
I hear ya. I think the key thing about unsigned types is that
how they behave doesn't match the unconscious expectations
people have for signed types. The two sets are just different.
IME it isn't hard to make the adjustment once one realizes
that different logical rules (ie, for how to write expressions)
are needed in the two cases.
 
By the way, if you wanted to use the subtraction form but still
get correct answers for the domain mentioned, we can do that by
shifting the offset, thusly:
 
if(str1.length()-str2.length() + SIZE_MAX/2 < 12+SIZE_MAX/2) { ... }
 
I wouldn't advocate writing this particular expression in this
form, but I think the general pattern is a good one to know.
Louis Krupp <lkrupp@nospam.pssw.com.invalid>: Nov 22 05:02PM -0700

On Tue, 22 Nov 2016 14:52:04 -0700, Louis Krupp
>object, whether it's static or automatic or allocated by something
>other than malloc(), you can use placement new to run the object's
>constructor with calling malloc().
 
... that should have been "without calling malloc()."
 
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 23 12:27AM

On Tue, 22 Nov 2016 15:06:33 -0600
> over what new and delete normally do is going to save you. Maybe you
> have to only use specific locations, but similar to the previous
> question, why would that happen?
 
You have been given some good examples as regards the use of placement
new (custom memory allocators are somewhat orthogonal to that,
although you may well use placement new in your custom allocator).
Leaving aside things like shared memory, these uses of placement new
usually come down to efficiency.
 
Say you want to copy an array of some heavy-duty objects of class type
T into a new array allocated on the heap (heavy duty in the sense that
their constructors do some significant work). A simple implementation
may allocate the memory for the new array of T with the new expression,
which will default construct a T object in each element of the array,
and then copy the existing array into the new one with, say,
std::copy(). That will copy construct a new set of T objects over the
default constructed ones.
 
A better approach may be to allocate an uninitialized array using
std::malloc(), and then to use placement new to copy construct the T
objects from the existing array directly into that uninitialized array.
There is a standard algorithm which will do that for you -
std::uninitialized_copy(): that will call up placement new for each
element of the new array.
 
In C++11 on, you can use the same approach for local arrays constructed
on the stack. Using alignas<T>, you can allocate an array of char with
the correct alignment for your T objects and then copy construct the T
objects into the array with placement new and/or
std::unitialized_copy().
 
As another example, I don't think it is possible to construct a
reasonable circular buffer without using placement new, for the same
reasons.
 
Chris
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 23 12:57AM

On Wed, 23 Nov 2016 00:27:01 +0000
Chris Vine <chris@cv
> of the array, and then copy the existing array into the new one with,
> say, std::copy(). That will copy construct a new set of T objects
> over the default constructed ones.
 
Errm. That should say that T's copy assignment operator will copy
assign the existing array of T objects into the previously default
constructed ones.
"Christopher J. Pisz" <cpisz@austin.rr.com>: Nov 22 07:34PM -0600

On 11/22/2016 6:27 PM, Chris Vine wrote:
> reasonable circular buffer without using placement new, for the same
> reasons.
 
> Chris
 
So is this where I create my own "allocator" that is an argument to so
many STL types? Or is that not required? Or is it not required or related?
 
Are there any good tutorials where I can actually see the difference for
myself and a simple example of how it can be used?
asetofsymbols@gmail.com: Nov 22 10:23PM -0800

The problem with all your hll way of see, all garbage collector way to see: it is
you lost the contact from the
memory management (other than a simple instruction as add eax ebx)
But memory and the final instruction CPU execute are one subject for
the programmer
Juha Nieminen <nospam@thanks.invalid>: Nov 23 07:10AM

> Even after Googling placement new, I cannot fathom why I would use it.
 
std::vector and std::deque (and possibly some of the others) internally
use placement new, and it's essential to their functionality.
"Christopher J. Pisz" <cpisz@austin.rr.com>: Nov 23 02:58AM -0600

On 11/22/2016 3:06 PM, Christopher J. Pisz wrote:
> memory manager and if I am familiar with placement new.
 
> Nope. I think I am accustomed to more high level programming then what
> they are looking for. None the less, I'd like to learn about the concept.
 
SNIP
 
So, I've Googled and read a few articles tonight. I implemented a test
(found below) and holy crap. It makes a difference by a factor of 100 on
my machine with the current settings in Release. Going from 12 seconds
to .12 seconds is pretty darn significant. So, there is something to be
said for this.
 
The test I implemented was following an article from IBM. I don't
understand a couple of things that they did:
 
1) They allocate 1 element at a time, rather than numElements * sizeof
the object to be stored. Why?
 
Also, the trick they are using in allocating the size of the Data object
and treating it like a Node pointer seems to be essential to their
design. How can we change the source to allocate numElements * sizeof
the object instead, if we wanted to?
 
 
2) Given that they are allocating one element at a time, just up front,
why am I seeing a performance increase as drastic as I am?
 
3) I don't seem to get any performance increase if I change the pool
size from 32 to 1024. Why?
 
Here is the amalgamated source code:
 
 
// Common Library
// #include "PerformanceTimer.h"
 
// Standard Includes
#include <iostream>
 
//------------------------------------------------------------------------------
const size_t g_numElements(10000);
const size_t g_iterations(5000);
const size_t g_poolSize(32);
 
//------------------------------------------------------------------------------
// Data class with no memory management
class NoMemoryManagement
{
public:
NoMemoryManagement(double realPart, double SpecificToSimpleMMPart);
 
private:
 
double m_realPart;
double m_complexPart;
};
 
//------------------------------------------------------------------------------
// Data class that overwides operator new and delete to use simple
memory managemer
class SpecificToSimpleMM
{
public:
SpecificToSimpleMM(double realPart, double SpecificToSimpleMMPart);
 
void * operator new(size_t size);
void operator delete(void * pointerToDelete);
 
private:
 
double m_realPart;
double m_complexPart;
};
 
//------------------------------------------------------------------------------
class IMemoryManager
{
public:
virtual void * allocate(size_t) = 0;
virtual void free(void *) = 0;
};
 
//------------------------------------------------------------------------------
// Memory Manager that allocates space for multiple objects, of a
specific type, at a time
//
// Customized for objects of type SpecificToSimpleMM and works only in
single-threaded environments.
// Keeps a pool of SpecificToSimpleMM objects available and has future
allocations occur from this pool.
class SimpleMemoryManager : public IMemoryManager
{
// Node in the memory pool
struct FreeStoreNode
{
FreeStoreNode * m_next;
};
 
void expandPoolSize();
void cleanUp();
 
// The memory pool
FreeStoreNode * m_freeStoreHead;
 
public:
 
SimpleMemoryManager();
virtual ~SimpleMemoryManager();
 
virtual void * allocate(size_t);
virtual void free(void *);
};
 
//------------------------------------------------------------------------------
NoMemoryManagement::NoMemoryManagement(double realPart, double complexPart)
:
m_realPart(realPart)
, m_complexPart(complexPart)
{
}
 
//------------------------------------------------------------------------------
SimpleMemoryManager g_memoryManager; // Global yuck! Just following the
online example
 
//------------------------------------------------------------------------------
SpecificToSimpleMM::SpecificToSimpleMM(double realPart, double complexPart)
:
m_realPart(realPart)
, m_complexPart(complexPart)
{
}
 
//------------------------------------------------------------------------------
void * SpecificToSimpleMM::operator new(size_t size)
{
return g_memoryManager.allocate(size);
}
 
//------------------------------------------------------------------------------
void SpecificToSimpleMM::operator delete(void * pointerToDelete)
{
g_memoryManager.free(pointerToDelete);
}
 
//------------------------------------------------------------------------------
SimpleMemoryManager::SimpleMemoryManager()
{
expandPoolSize();
}
 
//------------------------------------------------------------------------------
SimpleMemoryManager::~SimpleMemoryManager()
{
cleanUp();
}
 
//------------------------------------------------------------------------------
void SimpleMemoryManager::expandPoolSize()
{
// The trick to the design of this memory manager is that each
element that is
// allocated is the larger of a FreeStoreNode pointer or the size
of the object being stored.
// In this case, we are storing SpecificToSimpleMM objects, which
will be larger than a pointer.
// So, while we treat elements as FreeStoreNode pointers, the void
pointer returned by operator new
// points to SpecificToSimpleMM objects.
size_t size = (sizeof(SpecificToSimpleMM) > sizeof(FreeStoreNode
*)) ? sizeof(SpecificToSimpleMM) : sizeof(FreeStoreNode *);
 
FreeStoreNode * head = reinterpret_cast<FreeStoreNode *>(new
char[size]);
m_freeStoreHead = head;
 
for (int i = 0; i < g_poolSize; i++)
{
head->m_next = reinterpret_cast<FreeStoreNode *>(new char[size]);
head = head->m_next;
}
 
head->m_next = 0;
}
 
//------------------------------------------------------------------------------
void SimpleMemoryManager::cleanUp()
{
// Only cleans up memory from the blocks we allocated that did not
get used yet
// Otherwise, we have to rely on the user deleting any object he
newed, as normal
FreeStoreNode * nextPtr = m_freeStoreHead;
 
for (; nextPtr; nextPtr = m_freeStoreHead)
{
m_freeStoreHead = m_freeStoreHead->m_next;
delete[] nextPtr; // remember this was a char array
}
}
 
//------------------------------------------------------------------------------
void * SimpleMemoryManager::allocate(size_t size)
{
if (!m_freeStoreHead)
{
expandPoolSize();
}
 
FreeStoreNode * head = m_freeStoreHead;
m_freeStoreHead = head->m_next;
 
return head;
}
 
//------------------------------------------------------------------------------
void SimpleMemoryManager::free(void * deleted)
{
FreeStoreNode * head = static_cast<FreeStoreNode *> (deleted);
head->m_next = m_freeStoreHead;
m_freeStoreHead = head;
}
 
//------------------------------------------------------------------------------
void RunNoMemoryManagement()
{
/*
// Start timer
Common::PerformanceTimer timer;
timer.Start();
*/
// Do the work
NoMemoryManagement * array[g_numElements];
 
for (int i = 0; i < g_iterations; i++)
{
for (int j = 0; j < g_numElements; j++)
{
array[j] = new NoMemoryManagement(i, j);
}
 
for (int j = 0; j < g_numElements; j++)
{
delete array[j];
}
}
/*
// Stop the timer
double secondsElapsed = timer.Stop();
std::cout << "Test with no memory management took " <<
secondsElapsed << " seconds.\n";
*/
}
 
//------------------------------------------------------------------------------
void RunSimpleMemoryManagement()
{
/*
// Start timer
Common::PerformanceTimer timer;
timer.Start();
*/
// Do the work
SpecificToSimpleMM * array[1000];
 
for (int i = 0; i < 5000; i++)
{
for (int j = 0; j < 1000; j++)
{
array[j] = new SpecificToSimpleMM(i, j);
}
 
for (int j = 0; j < 1000; j++)
{
delete array[j];
}
}
/*
// Stop the timer
double secondsElapsed = timer.Stop();
std::cout << "Test with simple memory management took " <<
secondsElapsed << " seconds.\n";
*/
}
 
//------------------------------------------------------------------------------
int main(int argc, char * argv[])
{
RunNoMemoryManagement();
RunSimpleMemoryManagement();
 
return 0;
}
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Nov 23 10:46AM

On Tue, 22 Nov 2016 19:34:50 -0600
> related?
 
> Are there any good tutorials where I can actually see the difference
> for myself and a simple example of how it can be used?
 
My examples (and the other ones that have been offered) are not
directly concerned with designing allocator objects for C++
containers, but there is a connection.
 
References to "custom memory allocation" can mean a number of different
things. The simplest is the class specific operator new, whereby you
provide static class versions of at least operator new(std::size_t) and
operator new[](std::size_t), and usually (to respect the expected
interface) of their nothrow counterparts. These operators should just
allocate uninitialized memory - when called, the new and new[]
expressions (as opposed to these operators new) will construct objects
of the relevant class type for you within the uninitialized memory that
these operators allocate.
 
With class specific operator new you also provide the equivalent
class specific operator delete and operator delete[] methods. In
addition, to respect the interface that a user of the class expects you
would normally provide a class specific placement new (operator
new(std::size_t, void*), but this should usually just forward to global
placement new (::new(std::size_t, void*)). This is because with
placement new the uninitialized memory is provided externally by the
caller, not by operator new. The memory just gets handed on.
 
Then there are custom allocator objects for containers and certain
other types in the standard library that are allocator aware, as you
have mentioned. The idea is similar: looked at in the round, the
allocate() methods allocate uninitialized memory and the deallocate()
methods deallocate it. std::allocator_traits also provides a default
construct() method for placing items in that memory, which just calls
placement new, and a default destroy() method for removing items, which
just calls the data item's destructor directly: these construct() and
destroy() methods are syntactic sugar for placement new and a
destructor call. An obvious example of their usage is
std::vector::reserve(). If you call that method, std::vector's
allocator will provide a region of uninitialized memory for the
vector. When you push items into the vector, objects will be
constructed in that memory by std::vector's implementation using
placement new.
 
I gave the example of a fixed size circular buffer. Any reasonable
implementation for generic data types will, when the buffer is
constructed, allocate uninitialized memory for the buffer. Items will
be pushed onto the buffer using placement new. When popped off, old
items will be destroyed by calling the data type's destructor directly.
The buffer's memory will constantly be re-used for the in-place
construction of data items and will not be deallocated until the buffer
itself is destroyed.
 
I am afraid I don't actually know of any good tutorials. There is a bit
about allocators in section 34.4 of TC++PL, 4th edition.
 
Chris
David Brown <david.brown@hesbynett.no>: Nov 23 01:18AM +0100

> their expertise and moral authority, sometimes for horrendous
> purposes."
 
> "the modern Left has become obsessed with silencing heretics."
 
No, I doubt if I would benefit from that. From your brief summary here,
and a quick look at the page, I can see absolutely no relevance to
anything I wrote, anything related to the thread, or anything related to
this newsgroup.
 
> stand up to them. They accuse me of "fussing," but I'm not
> the one trying to hide the elephant in the room -- the C++
> Middleware Writer.
 
I accuse you of fussing about swearing - needlessly, pointlessly,
repetitively, and counter-productively. I believe it is a perfectly
fair accusation, and I think everyone in this group who has expressed an
opinion on the matter feels roughly the same.
 
You are not a "voice standing up to me" - I rarely swear, and not
without good reason, and I don't like unnecessary or excessive swearing.
I just find your repeated moans about other people swearing to be far
more irritating than any swearing from others, and they invariably
provoke more swearing in response. It is a truly idiotic and annoying
habit you have, and I wish you would stop.
 
In general, of course, I am quite happy for you to express your opinion
as you want - and I have no desire to silence you (even if I had the
power to do so).
 
As for the C++ Middleware Writer being "the elephant in the room" -
well, I know the project means a lot to you, and I wish you every
success. But I don't know anyone else in this group that uses it or
thinks it is a useful idea, let alone the sort of revolutionary idea you
apparently believe. However, I have nothing against you talking about
it, and I cannot comprehend why you think I might have.
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: Nov 23 06:06AM +0100

On 23.11.2016 00:50, Stefan Ram wrote:
> implementations of the C++ programming language.«
 
> . This sentence already uses the verb »to specify«, and when
> someone does not know what this means, he needs to learn English.
 
Just trust me on this.
 
Or consult an English professor. ;-)
 
 
Cheers!,
 
- Alf
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: