- hidden static - 7 Updates
- the new {} syntax - 1 Update
- a problem about programming - 5 Updates
- Threading question - 1 Update
- "Use Stronger Types!" - 1 Update
- Custom Memory Manager & placement new - 8 Updates
- Failed interview test - 2 Updates
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:
Post a Comment