Jump to content

Wikipedia:Reference desk/Archives/Computing/2014 December 9

From Wikipedia, the free encyclopedia
Computing desk
< December 8 << Nov | December | Jan >> December 10 >
Welcome to the Wikipedia Computing Reference Desk Archives
The page you are currently viewing is an archive page. While you can leave answers for any questions shown below, please ask new questions on one of the current reference desk pages.


December 9

[edit]

C++ How to avoid copying temporary object when it's passed to a container function

[edit]
//  A big class in memory.
class CData
{
public:
   long long int mArray[40000];
};

std::list< CData > mylist;
mylist.push_back(CData());

I know I can use pointer like std::list< CData * > mylist; mylist.push_back(new CData()); But except using pointers/references, is there any way to avoid the temporary CData get copied (so program runs faster) when passed to a container? That is, I want to add things into a container in place. ...maybe override the copy constructor of CData like this:

#include <list>

//  A big class in memory.
class CData
{
public:
 
  CData() {} // Dummy default constructor. Don't initialize at all but after the object has been added to a container.

  CData(const CData & obj) {} // Dummy copy constructor. Don't copy at all.
 
  long long int mArray[40000];
};
 
int main()
{
  std::list< CData > mylist;
  mylist.push_back(CData());

  //  Initialize the object in place after it has been added to a container.
  long long int * pArray = mylist.back().mArray;
  for (unsigned int i = 0; i < 40000; i++)
    pArray[i] = i;    
  
  return 0;
}

Could someone confirm this? - Justin545 (talk) 04:46, 9 December 2014 (UTC)[reply]

In C++11, you can use emplace_back() instead of push_back(). Pre-C++11, I'm not sure there's any clean way to do this. My best suggestion would be to use a boost::optional<CData>, or implement similar functionality in your class. (Neutering the copy constructor as you did is asking for trouble, but you could add a bool initialized; that's set to false by the default constructor and to true by an initialize() method, and copy the data only if it's true.) You could also use a third-party list class that has emplace_back() or an equivalent. -- BenRG (talk) 09:10, 9 December 2014 (UTC)[reply]
Took a look at your boost::optional<CData> document, and I accidentally found that boost also provides a family of InPlaceFactories, which overrides C++'s placement new operator and I think it may be another choice for pre-C++-11. The placement new operator get overridden so we can create a instance of CData at a specified address (in our case, the address of CData object in a list node), adding a wrapper class so we can keep CData intact as far as we can:
long code sample
#include <cstddef> // For std::size_t.
#include <list>
#include <iostream>
 
class CDummyArg {}; // Special type enabling ad-hoc polymorphism for CData's constructor.
 
class CData
{
 
public:
 
  CData(CDummyArg) { std::cout << "CData (" << this << "): A dummy constructor allowing minimized CPU cost of instance creation." << std::endl; } 
 
  CData() { std::cout << "CData (" << this << "): This is the default constructor, do whatever you want here!" << std::endl; }
 
  CData(unsigned int indexBegin, unsigned int indexEnd)
  {
    std::cout << "CData (" << this << "): Constructor with 2 arguments." << std::endl;
    for (unsigned int i = indexBegin; i < indexEnd; i++)
      mArray[i] = i;    
  }
 
  long long int mArray[40000];
 
};
 
//  A class to hold arguments passed to CData's constructor.
class CTypedInPlaceFactory2
{
 
public:
 
  CTypedInPlaceFactory2(unsigned int const & indexBegin, unsigned int const & indexEnd)
    : mIndexBegin(indexBegin), mIndexEnd(indexEnd) {}
 
  //  Construct CData at the given address pAddr.
  void construct(void * pAddr) const { new (pAddr) CData(mIndexBegin, mIndexEnd);/* call the overridden new operator */ }
 
  void * operator new(std::size_t count, void * ptr) { return ptr; } // The overridden placement new operator.
 
private:
 
  unsigned int mIndexBegin;
  unsigned int mIndexEnd;
 
};

class CWrapper
{

public:  

  CWrapper() : mWrapped(CDummyArg())
  {
    std::cout << "CWrapper (" << this << "): Dummy default constructor. Don't initialize at all. (but after the object has been added to a container)" << std::endl;
  }

  CWrapper(const CWrapper & obj) : mWrapped(CDummyArg()) { std::cout << "CWrapper (" << this << "): Dummy copy constructor. Don't copy at all." << std::endl; }

  CData mWrapped;

};
 
int main()
{
  std::list< CWrapper > mylist;
 
  mylist.push_back(CWrapper());
  CTypedInPlaceFactory2(0, 40000).construct(&mylist.back());
  std::cout << "Wrapper obj. at back of mylist: " << &mylist.back() << std::endl;
  std::cout << "Element at 2014: " << mylist.back().mWrapped.mArray[2014] << std::endl;
 
  return 0;
}

the output is

CData (0xbfcf0e00): A dummy constructor allowing minimized CPU cost of instance creation.
CWrapper (0xbfcf0e00): Dummy default constructor. Don't initialize at all. (but after the object has been added to a container)
CData (0xb73b0010): A dummy constructor allowing minimized CPU cost of instance creation.
CWrapper (0xb73b0010): Dummy copy constructor. Don't copy at all.
CData (0xb73b0010): Constructor with 2 arguments.
Wrapper obj. at back of mylist: 0xb73b0010
Element at 2014: 2014

- Justin545 (talk) 09:47, 10 December 2014 (UTC)[reply]

You are essentially doing
    CData obj(CDummyArg());
    new (&obj) CData(0, 40000);
in other words, constructing the object twice in the same memory. I'm not sure this has defined behavior for non-POD types (and pre C++11, anything with an explicit constructor is not POD). If you want to do it this way, at least do
    CData obj(CDummyArg());
    obj.~CData();
    new (&obj) CData(0, 40000);
in other words, convert the object back to raw memory before re-constructing it. I think this is well behaved.
Incidentally, I don't see the point of the CTypedInPlaceFactory2 class. You might as well pass the indexBegin and indexEnd arguments directly to construct(), and at that point you can just make construct() a function and discard the class. Also, the member operator new isn't doing anything. It would only be used if you were constructing CTypedInPlaceFactory2 with placement new, and even then it would do the same thing as the default placement new. Overall your solution seems too complicated. There may be no non-hacky way to do this pre-C++11, but if you're going to use a hack you should write it in a hacky way, with a comment saying "this is a hack", not disguise it in layers of abstraction. -- BenRG (talk) 22:33, 10 December 2014 (UTC)[reply]
According to the non-POD issue you've recognized, I've changed the CWrapper so it has better behavior in constructing/destructing. CWrapper is renamed as CSpacer and it no longer includes CData as its member (so we don't need CData(CDummyArg) anymore), but it becomes a template class holding a buffer and create/destroy CData obj in the buffer instead (that is, to move the definition of CData's construction outside the container so that the user can redefine it):
template< class TValue >
class CSpacer
{
public:

  // ...
 
  //  2 members (which introduce extra data overhead) to manage the mBuffer as the placeholder for CData obj.
  bool mInitialized; // State of mBuffer, which is a piece of raw memory when it's false.
  const CInPlaceFactory * mpInPlaceFactory; // Act as non-regular object when non-NULL and get passed to copy constructor. Degrading to normal object when it's NULL.
 
  char mBuffer[sizeof(TValue)]; // Place to put CData object. 
 
};
This way, I think it's less like a hack. And CSpacer has a constructor accepting in-place factory obj as its argument:
  CSpacer(const CInPlaceFactory & inPlaceFactory) : mInitialized(false), mpInPlaceFactory(&inPlaceFactory) {}
so I can use in-place factory in a more sensible way:
int main()
{
  CList< CData > list;
  // ...
  list.push_back_inplace(CInPlaceFactory2< CData, size_t, size_t >(0, 40000));
  // ...
}
The complete code after revision is at the bottom. Sadly, the code is even more complicated this time ...
Go back to C++11. I know pretty much nothing about C++11 since [1] I don't know if it's a stable standard or just a draft under development [2] it's pretty new (C++ 2011) and is supposed to be unsupported in many platform/environment [3] I don't know if it's worth to learn unless it will become popular and not be abandoned in the long run. But I have took a look since you had mentioned after all. I found C++11 has "move" constructor and the example of it:
class MemoryBlock
{
public:

   // ...

   //  The move constructor
   MemoryBlock(MemoryBlock&& other)
      : _data(NULL)
      , _length(0)
   {
      std::cout << "In MemoryBlock(MemoryBlock&&). length = " 
                << other._length << ". Moving resource." << std::endl;
   
      // Copy the data pointer and its length from the 
      // source object.
      _data = other._data;
      _length = other._length;
   
      // Release the data pointer from the source object so that
      // the destructor does not free the memory multiple times.
      other._data = NULL;
      other._length = 0;
   }

   // ...

private:
   size_t _length; // The length of the resource.
   int* _data; // The resource.
};
It turns out the move constructor just copies members from RHS obj 'other' and invalid the members in RHS obj. If so it seems that the move constructor won't be much much faster than copy constructor. But the web page states:
A move constructor enables you to implement move semantics, which can significantly improve the performance of your applications.
I don't know if emplace_back() is based on the move constructor or some other mechanism to do in-place construction. Which would be the question I need to resolve before I can start using emplace_back() confidently...
Here is the complete code after revision:
#include <iostream>
#include <list>
#include <boost/utility/typed_in_place_factory.hpp>

class CData
{

public:

  CData() { std::cout << "CData (" << this << "): This is the default constructor, do whatever you want here!" << std::endl; }
 
  CData(size_t indexBegin, size_t indexEnd)
  {
    std::cout << "CData (" << this << "): Constructor with 2 arguments." << std::endl;
    for (size_t i = indexBegin; i < indexEnd; i++)
      mArray[i] = i;    
  }

  CData(size_t indexBegin, size_t indexEnd, long long int value)
  {
    std::cout << "CData (" << this << "): Constructor with 3 arguments." << std::endl;
    for (size_t i = indexBegin; i < indexEnd; i++)
      mArray[i] = value;
  }

  ~CData() { std::cout << "CData (" << this << "): destructor, do whatever to destroy the object." << std::endl; }

  long long int mArray[40000];

};

//  For example, class 'typed_in_place_factory3' defined in 'typed_in_place_factory.hpp' is:
//
//      template< class T , class A0 , class A1 , class A2 >
//      class typed_in_place_factory3
//        :
//        public typed_in_place_factory_base
//      {
//      public:
//      
//        typedef T value_type;
//      
//        explicit typed_in_place_factory3
//            ( A0 const& a0 , A1 const& a1 , A2 const& a2 )
//      
//          : m_a0 ( a0 ) , m_a1 ( a1 ) , m_a2 ( a2 )
//      
//        {}
//      
//        void* apply (void* address) const
//        {
//          return new(address) T( m_a0 , m_a1 , m_a2 ); // Don't need to overload operator new since there has already a predefined one.
//        }
//      
//        void* apply (void* address, std::size_t n) const
//        {
//          for(void* next = address = this->apply(address); !! --n;)
//            this->apply(next = static_cast<char *>(next) + sizeof(T));
//          return address;
//        }
//      
//        A0 const& m_a0; A1 const& m_a1; A2 const& m_a2;
//      };
//
//  Similarly, class typed_in_place_factoryN accepts N number of arguments A0, ..., A(N - 1).

class CInPlaceFactory
{

public:
  
  virtual void * construct(void * address) const = 0;

};

template< class T , class A0 , class A1 >
class CInPlaceFactory2 : public CInPlaceFactory
{

public:
  
  explicit CInPlaceFactory2(A0 const & a0, A1 const & a1) : mTypedInPlaceFactory(a0, a1) {}
  
  void * construct(void * address) const { return mTypedInPlaceFactory.apply(address); }

  boost::typed_in_place_factory2< T, A0, A1 > mTypedInPlaceFactory;

};

template< class T , class A0 , class A1, class A2 >
class CInPlaceFactory3 : public CInPlaceFactory
{

public:
  
  explicit CInPlaceFactory3(A0 const & a0, A1 const & a1, A2 const & a2) : mTypedInPlaceFactory(a0, a1, a2) {}
  
  void * construct(void * address) const { return mTypedInPlaceFactory.apply(address); }

  boost::typed_in_place_factory3< T, A0, A1, A2 > mTypedInPlaceFactory;

};

template< class TValue >
class CSpacer
{

public:

  CSpacer() : mInitialized(false), mpInPlaceFactory(NULL) {} // Default constructor. Don't initialize CData obj in mBuffer at all.
 
  //  Copy constructor. Don't copy CData obj in mBuffer at all when the RHS obj holds a valid in-place factory and construct in place instead.
  CSpacer(const CSpacer & obj) : mInitialized(false), mpInPlaceFactory(NULL)
  {
    if ((!obj.mInitialized) && (!obj.mpInPlaceFactory)) return;

    if (obj.mpInPlaceFactory) {
      //  Constructing in place.
      obj.mpInPlaceFactory->construct(mBuffer); // Construct CData object in place by calling placement new operator.
      mInitialized = true;
    } else {
      //  Copy-constructing in usual sementics.
      ::new(mBuffer) TValue(reinterpret_cast< const TValue & >(*obj.mBuffer)); // Use the predefined placement new to call CData's copy constructor.
      mInitialized = obj.mInitialized;
    }   
  } 

  CSpacer(const CInPlaceFactory & inPlaceFactory) : mInitialized(false), mpInPlaceFactory(&inPlaceFactory) {}

  CSpacer & operator=(const CSpacer & obj)
  {
    if ((!obj.mInitialized) && (!obj.mpInPlaceFactory)) return;

    if (mInitialized)
      reinterpret_cast< TValue * >(mBuffer)->~TValue(); // Call CData's destructor.

    if (obj.mpInPlaceFactory) {
      //  Constructing in place rather than assigning.
      obj.mpInPlaceFactory->construct(mBuffer); // Construct CData object in place by calling placement new operator.
      mInitialized = true;
    } else {
      //  Assinging in usual sementics.
      reinterpret_cast< TValue & >(*mBuffer) = reinterpret_cast< const TValue & >(*obj.mBuffer); // Call CData's operator=().
      mInitialized = obj.mInitialized;
    }   
  }

  ~CSpacer()
  {
    if (!mInitialized) {
      mpInPlaceFactory = NULL;
      return;
    }
    reinterpret_cast< TValue * >(mBuffer)->~TValue(); // Call CData's destructor.
    mpInPlaceFactory = NULL;
    mInitialized = false;
  }

  //  2 members introduce extra data overhead.
  bool mInitialized; // State of mBuffer, which is a piece of raw memory when it's false.
  const CInPlaceFactory * mpInPlaceFactory; // Act as non-regular object when non-NULL and get passed to copy constructor.

  char mBuffer[sizeof(TValue)]; // Place to put CData object. 

};

template< class TValue >
class CList : public std::list< CSpacer< TValue > >
{

public:

  typedef std::list< CSpacer< TValue > > TBase;

  void push_back_inplace(const CInPlaceFactory & inPlaceFactory)
  {
    TBase::push_back(CSpacer< TValue >(inPlaceFactory));
  }

  TValue & front()
  {
    return reinterpret_cast< TValue & >(*TBase::front().mBuffer);
  }

  TValue & back()
  {
    return reinterpret_cast< TValue & >(*TBase::back().mBuffer);
  }

};

#define CHK(x) { std::cout << "== checkpoint (" << x << ") ==" << std::endl; }

int main()
{
  CList< CData > list;
  CHK(1);
  list.push_back_inplace(CInPlaceFactory3< CData, size_t, size_t, long long int >(0, 40000, -1213));
  CHK(2);
  list.push_back_inplace(CInPlaceFactory2< CData, size_t, size_t >(0, 40000));
  CHK(3);
  std::cout << list.front().mArray[123] << std::endl;
  CHK(4);
  std::cout << list.back().mArray[123] << std::endl;
  CHK(5);
  list.pop_front();
  CHK(6);
  list.pop_front();
  CHK(7);
  
  return 0;
}

the output is:

== checkpoint (1) ==
CData (0xb745b018): Constructor with 3 arguments.
== checkpoint (2) ==
CData (0xb740c018): Constructor with 2 arguments.
== checkpoint (3) ==
-1213
== checkpoint (4) ==
123
== checkpoint (5) ==
CData (0xb745b018): destructor, do whatever to destroy the object.
== checkpoint (6) ==
CData (0xb740c018): destructor, do whatever to destroy the object.
== checkpoint (7) ==

- Justin545 (talk) 12:55, 13 December 2014 (UTC)[reply]


A keyboard-oriented configurable Linux-distro

[edit]

My old computer crashed, which at last forces me to search for an alternative for Linux Mint 11. :)
I have been using Linux for over a decade now, but it appears to become ever more user-unfriendly. At least, for me, in the sense that it is getting more mouse-oriented and rigid. First Suse, and then Mint, the two distro's I have used most.
What I want is a keyboard-oriented OS, to prevent rsi, and for speed of operation. Mind you, I don't mean a console, but keyboard shortcuts and the use of tab and arrows for navigation and selection.
Also, I want it to be as configurable as possible. I liked KDE for that reason, but years ago that also became too rigid.

For example, I liked the system menu of Win98: Win-key, a few arrow keys and Enter. That way I could start most applications in less than a second. And blind, whithout having to give it any thought, with the most used applications where I wanted them (and always in the same place).

Mind you, I know more about computers than most people (for example, I know the basics of partitioning and why /home should be on a separate partition), but I am certainly not a computer expert. For example, someone suggested Slackware, but even reading about the installation process confuses me.

I don't care much how it looks at first, it may even be msWindows-style, as long as I can adapt it easily (with GUIs).
Also, I want it to stay that way in the future, unlike Suse and Mint (which is another reason Slackware was suggested to me).

Try Ubuntu, --AboutFace 22 (talk) 17:10, 10 December 2014 (UTC)[reply]
Look for distributions with "Mate Desktop". Mate is based on Gnome Desktop 2.x. --Hans Haase (talk) 08:13, 13 December 2014 (UTC)[reply]

Audacity question

[edit]

In the Audacity audio editor, how do you take two tracks and use one as a modulating signal to produce an amplitude-modulated version of the other track? (Preferably the resulting signal should be in a new track.) Thanks. — Preceding unsigned comment added by 134.242.92.2 (talk) 15:04, 9 December 2014 (UTC)[reply]

You can use the built-in Vocoder effect. The vocoder is a modulator; mathematically, it is equivalent to a mixer (in the technical sense that means a frequency mixer; not to be confused with the non-technical sense of a general "software audio mixer"). At the core, this effect multiplies signal 1 by signal 2, with a few additional tunable parameters, which is equivalent to "modulation" in the sense of amplitude modulation. Nimur (talk) 21:57, 9 December 2014 (UTC)[reply]

caching problem with date-sensitive page content

[edit]

There is a problem with date sensitive code used in the article Stratford Shakespeare Festival.

Under "years active" (see box at top right), it was purporting to calculate the time elapsed since a particular date, but was showing the result incorrectly as being 9 days less than it actually was. After I assumed initially that it was either a caching problem at my end or an error in the calculation, it turns out that it was a caching problem at the server end, or at least I assume so because when I found the instructions on how to purge pages it seemed to solve it. However, it felt like a voyage into very obscure depths of the system, and clearly the reader cannot be expected to muck about like that just to see the accurate information. The software needs to be set up so that where a page has any content based on the current date, any cache should expire at the end of the day. (There could possibly be some time-zone related issues to think about too, depending on whether people can reasonably quibble over a few hours, but certainly it shouldn't be wrong by 9 days.)

I am not very confident whether this is the correct page to report this, but please could somebody pass it onto the right people if it isn't. Thanks.

--Money money tickle parsnip (talk) 18:18, 9 December 2014 (UTC)[reply]

Perhaps if there is a problem with Template:How long ago, the correct place to discuss that would be Template talk:How long ago. Problems have been reported in the past. Nimur (talk) 22:06, 9 December 2014 (UTC)[reply]

But if I have understood correctly, the Template page you pointed me to is just where the calculation is performed, and the errors previously discussed on the Template Talk page relate to the calculation itself. This is not that sort of error: it seems that the calculation is now being done correctly, but the results are being cached for longer than appropriate given the nature of the content. This must surely be a more general issue with how the server handles pages that use real-time information. As I stated, after following the page purge instructions the result was fine. The same issue has arisen again today: I looked, and it told me "61 years, 4 months and 26 days", which was correct yesterday. I followed the purge instructions again (click edit and then change "edit" to "purge" in the address bar), and now it says "... and 27 days", which is correct today. There is no evidence of anything needing to be fixed in the Template.

-- Money money tickle parsnip (talk) 08:58, 10 December 2014 (UTC)[reply]