mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

What if it didn't implement copy con or move con and the compiler generated it, would I still use copy/swap idiom?

The compiler will also generate the copy- and move-assignment operators for you. However, if you have data members like a vector, you would still benefit from using a copy-and-swap implementation.

There is little point in using the copy-assignment operator that you have posted, because that is basically the same as what the compiler will generate for you. So, usually, if the compiler-generated copy- and move-constructors are OK, then the compiler-generated copy- and move-assignment operator are also OK.

The next thing I read is that I should not (should not have to) implement copy-swap or copy/move constructors for POD types and vector based classes where the underlying type is just simple. Is this true?

Yes. For POD-types, there is no point in creating your own copy- or move- functions (construct and assignment), because the compiler-generated versions are fine.

Does that mean my test class is bad since I used a vector and did the swap on it?

No. If you have something like a container (e.g., vector) which can benefit (performance-wise) from the use of a swap function, then you can benefit from defining your own swap and copy-and-swap functions. However, with the new standard and its move-semantics, the default swap function is pretty good already (because it relies on the move-assignment operators), so the need to implement your own swap is reduced.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The problem is you test program, the following three lines:

Bitmaps A = Bitmaps("C:/Meh.bmp");
Bitmaps B(Bitmaps("C:/Meh.bmp"));
Bitmaps C = A;

The first line will call the constructor that takes a string (or char pointer). That is simply the syntax to call an explicit constructor. The second line does the same thing as the first. And the third line is one way to construct an object using the copy-constructor.

If you want to invoke the copy-assignment operator, you should do:

Bitmaps D;  // default-construct it (or construct it otherwise, doesn't matter).
D = A;      //  then, you do the assignment.

If you want to invoke the move-constructor, you should do either of these things:

Bitmaps E(std::move(A));   // the std::move marks A as "to-be-moved".
Bitmaps F = std::move(A);

If you want to invoke the move-assignment operator, you should do this:

Bitmaps G;
G = std::move(A);
// Or, you can also do:
G = Bitmaps("C:/Meh.bmp");  // assign a temporary (rvalue).

And, by the way, if you have a copy-and-swap assignment operator which passes the object by value (which is correct), then you don't need a separate move-assignment operator because the copy-and-swap will also act as a move-and-swap if the passed value-object is constructed from the move-constructor (instead of the copy-constructor used in the copy-and-swap).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

That never occured to me that such a thing can be done. I have never seen such code before.

The non-copyable idiom is a classic technique in C++. It is not used as much as it should, but it is a nice technique, it is one of those nice and simple examples of how you can get the compiler to help you to write correct code (by forcing it to produce compilation errors when the code is not correct).

With that implementation, is it still safe to supply a move constructor?

Yes. The move constructor generally makes sense for this case. You might not really need it though, but it doesn't hurt to have one. If you want to get more or less the same behavior as move-semantics, but remain legal C++03 (previous standard), then you need to define a default constructor (null handle) and a swap function.

Towards the end of my tutorial on RAII in C++11, there is a non-copyable class example (a home-brewed array container).

Should I supply one or is it just not necessary?

It is not necessary, unless you need to move the object around, but it doesn't hurt to have it anyways (and you never know, you might want to have a container of such objects (like a thread-pool), which is possible even if the class only has move-semantics, with C++11 enabled STL containers).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The HANDLE is what one calls an "opaque pointer". This just means that it is essentially a pointer but you have no idea what it points to, and you're not supposed to know (it's "opaque").

If you library from which you got the opaque pointer does not have a method to copy (or duplicate) the resource that it points to, then you cannot copy it, and you shouldn't attempt to.

If you should copy something, then you must disable copying. This can be done in a number of ways, depending on what you've got to work with. The basic technique is to declare the copy-construct as a private member function and not provide a definition for it, so that any attempts to call the copy-constructor is going to result in a compilation error:

class Foo {
  private:
    Foo(const Foo&);  // private copy-con, no definition (declaration-only).
    Foo& operator=(const Foo&);  // private copy-assign, no definition either.
  public:
    //... the rest of the class.
};

Another possibility is to inherit from boost::noncopyable which is basically just a simple class like the Foo class above, that declares private copy-con and copy-assign. Finally, if you have a C++11 compiler (as I assume from your mentions of move-constructors), you can use the explicit deletion of the constructor, as follows:

class Foo {
  public:
    Foo(const Foo&) = delete;  // non-copyable
    Foo& operator=(const Foo&) = delete;  // non-copy-assignable
};

The effect is the same as with a private, undefined copy-con and copy-assign, but the …

triumphost commented: PERFECT! +5
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

First of all, the scan() function should take the vector by reference, to avoid unnecessary copying. That is, as so:

bool scan( std::vector< unsigned char >& data );  // notice the '&' here.

If the memcpy "breaks" (would be useful to know what the error is!), it is most likely a problem of bounds. Did you use the resize() function before doing the memcpy, to make sure the vector has the needed size for the elements that you are copying into it. Also, when reading binary data from a file, you need to make sure the endianness is correct and that you use "fixed-size integer types". Types like short or int are not fixed in size (the size varies between platforms), you should use types in the header <stdint.h> which include types like int16_t or uint32_t, for 16-bit signed integers and 32-bit unsigned integers, respectively.

If I remember correctly, the 3ds file format has a "chunk-size" field, I hope that your file format has that too because it is very important. You should make sure to check that your mem-copies do not exceed the chunk-size, to make sure things are consistent all the time.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Yeah, you use a thing called "placeholders". In Boost, these are _1, _2, etc. The number represents which parameter is passed there, so you can even change the order if you want, and you can fix some parameters and leave others to be passed to the function when it is called. As so:

#include <boost/bind.hpp>
#include <iostream>

void func(int i, int j) {
  std::cout << i << " " << j << std::endl;
};

int main() {
  boost::function<void(int,int)> f;

  f = boost::bind(&func, _1, _2);
  f(42,69);

  f = boost::bind(&func, _2, _1);
  f(42,69);

  boost::function<void(int)> f2;

  f2 = boost::bind(&func, 0, _1);
  f2(42);

  f2 = boost::bind(&func, _1, 0);
  f2(42);

  return 0;
};

Read the Boost.Bind docs for more examples of what you can do.

DeanMSands3 commented: Nice! I need to look into that! +4
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

First of all, welcome to Daniweb!

Second, your code is not remotely as horrible as you say it is (I've seen a lot worse). It is pretty good overall, with a few issues to solve.

Ok, so, on to the issues:

1)
You have a memory leak in the load function. The function allocates an array of unsigned chars for dumping the file content into, before scanning it. That array is never deallocated. The load function really should have the type bool as output (you even have a return false; statement), and there is no point in returning an unsigned char. Also, because you have two exit points (one from an error, returning false, and one from reaching the end without errors), you would need to delete the array at both of these points. That is annoying to do, it is much easier to use an STL container instead to store the array, this way, it will automatically be cleaned up when you exit the function (from whichever exit-point). So, this corrected version would make more sense:

bool sdObj::load(string const &filename)  // return 'bool'
{
    //unsigned char *funcObjData = NULL;
    vector< unsigned char > funcObjData;  // use a vector container instead of array.

    ifstream infile(filename.c_str(), ios::binary | ios::in);

    if(!infile.is_open())
        return false;     // this is a fail, right?

    infile.seekg(0, ios::end);
    filesize = (long)infile.tellg();
    infile.seekg(0, ios::beg);

    /*if(funcObjData != NULL)
    {
        delete[] funcObjData;
        funcObjData = NULL;
    }*/ // This check is not useful because funcObjData is local.
        // Your compiler should …
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

random --> generator

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

It's the ghost of cscgal still haunting the forums... hooouuuu...

No, I think it is just that the last-poster's name is not linked to the user profiles (as it doesn't have clickable link to the profile). But, I think it shows up correctly everywhere else including article titles of articles started by Dani (before her reincarnation).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

We will not do your homework for you. Have you tried? What code do you have so far? Do you have a specific problem? You have to show some efforts before we can help.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

We will not do your homework for you. Have you tried? What code do you have so far? Do you have a specific problem? You have to show some efforts before we can help.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Code optimization and time-complexity are separate issues (at least, IMO).

Algorithmic complexity

Time-complexity just means to analyse what is the relationship between the size of the data and the amount of time it will take to perform a given operation (algorithm) on that data. Typically, N usually represents the size of the data, like the number of elements in an array, number of nodes in a graph, number of words in a dictionary, or number of entries in a database, and so on. Of course, sometimes there are more than one such number to represent the size of the data, in which case other letters are used. Basically, the Big O just represents the fact that we don't care about the actual time that it takes (e.g., how many seconds to perform the algorithm on 1000 elements), but rather only care about the biggest functional relationship between time and data-size. So, if you see O(1), which we call "constant-time", it really means that the time taken to perform the algorithm is the same (constant) regardless of the amount of data. If you see O(N), which we call "linear-time", it means that the time taken to perform the algorithm grows as linear function of the size of the data (i.e., twice the size equal twice the time). And so on, you'll see things like O(N^2) (quadratic-time) or O(N^3) for more complex algorithms. The reason why you won't see things like O(N^2 + N + 1) is because there is usually an …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I have to admit that I always found "cscgal" to be a rather poor choice, but who am I to speak, right?

Changing it to Dani would certainly make it more obvious who you are (although "The Queen of Daniweb", "Administrator", your post count and join-date says a lot already). At the same time, just "Dani" doesn't have a lot of pizazz either (I'm not saying you have an ugly name, I'm just saying people usually enhance their name with a cool twist, or choose a cool alias).

Of course, I'm not the one to talk, my username is pretty bad too, but now that it can be changed, I just might, when I think of something appropriate.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The homepage screenshot is nice, but in my profile it shows the screenshot of my department's homepage (Center for Intelligent Machines, McGill), not my homepage. Maybe it is due to the tilde sign in the URL:
http://cim.mcgill.ca/~mpersson

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

And, you should make the base-class desctructor virtual, as so:

class Digitize
{
 protected:
    int * number;
    int Digits;
 public:
    Digitize(_uint64);
    virtual ~Digitize() {delete [] number;} // notice the 'virtual' here.
    void Display();
};
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

If you like the std::pair solution, then you should be aware of a neat little function called std::tie (also available in Boost, for pre-C++11, as boost::tie). This function basically constructs a pair (or generally, a tuple) of references to its given parameters. This allows for this type of code:

MemberRawArrayVector my_vect;
MemberRawArrayVector::iterator it, it_end;  // declare two iterators.
for(std::tie(it,it_end) = my_vect.range(); it != it_end; ++it) {
  /*  do whatever  */
};

This tie function is pretty useful for all sorts of schemes where you return a tuple (or pair) from a function (as opposed to the traditional solution of giving pass-by-reference parameters to store additional result-values of a function).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I think you need a little lesson in common practices and idiomatic C++ programming.

In the first class with vectors, it returns the member array and it is read only to prevent resizing. Thing is, I want to be able to modify the values in it without the SetMemberValue function.

First of all, in any case, you probably don't want to return a pointer (or reference) to the vector because that just exposes too much of the internals of your class. As for what you want to achieve, let me think, you want to be able to access all the elements in the vector, be able to modify them, but not be able to resize the vector. There is a well established practice to do exactly that, it is called "iterator ranges". Typically, you would do this:

class MemberRawArrayVector
{
    private:
        vector<unsigned char> MemberRawArray;
    public:
        typedef vector<unsigned char>::iterator iterator;
        typedef vector<unsigned char>::const_iterator const_iterator;

        MemberRawArrayVector(int Size) : MemberRawArray(Size) {}
        void SetMemberValue(char Value, int Index) { MemberRawArray[Index] = Value; }
        // either this:
        iterator begin() { return MemberRawArray.begin(); };
        const_iterator begin() const { return MemberRawArray.begin(); };
        iterator end() { return MemberRawArray.end(); };
        const_iterator end() const { return MemberRawArray.end(); };

        // or this (or both):
        std::pair< iterator, iterator > range() { 
            return std::make_pair(MemberRawArray.begin(), MemberRawArray.end()); 
        };
        std::pair< const_iterator, const_iterator > range() const { 
            return std::make_pair(MemberRawArray.begin(), MemberRawArray.end()); 
        };
};

Just about any functions that you might want to write that does anything with the elements in an array (or list, or …

triumphost commented: +1 Thinks way outside of the box! Excellent answer! +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The two problems I see (besides the lack of indentation ;) ) is that you don't initialize the value of count and you have one too many iterations. A more correct code would be this:

bigNum = 1;
count = 0;
while(count++ < power)
  bigNum *= num;

Or, even better, if you try to stick with more idiomatic (or "usual") code, you should write:

int bigNum = 1;  // initialize, at the site of first use (initialization).
for(int count = 0; count < power; ++count)  // use a for-loop when you count iterations.
  bigNum *= num;

Sticking to conventional coding style is not just for esthetics, it also helps you and others to understand what you are doing, and bugs are easier to find too (because you easily see missing pieces, and formulating idiomatic code becomes a second nature).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Is this, and all your other threads, entries to the code snippet contest?

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Hi Frank,
It's a bit funny that you resurrect your presence in forums by resurrecting a 6 year-old thread (which we usually frown upon). Anyways, welcome to Daniweb!

I should bow my head to you as I started my programming life with your products. Back when Borland still carried time-unlimited trial-versions of Borland Delphi (back in late 90s), it was an awesome tool for me as a teenager. I programmed in Delphi for probably about 6 years and enjoyed that very much. The Delphi language (object-pascal) and the RAD tools of Borland were really awesome products. For a while, I was a strong advocate of Delphi as a language and as a RAD tool. I'm now almost exclusively programming in C++ (with some C and Python), but I still have not found any RAD/GUI tool that even comes close to Delphi's VCL (except maybe the C++Builder version of VCL). So, thank you very much sir, without your awesome products I may never have got so into programming.

Welcome to Daniweb, hope you'll stick around!

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Which is the best to use?

It really depends on the case. Explanation below.

If I use const T&, will it create temporaries every single time?

No. The whole point of it is that it won't create any temporaries. However, it will cause indirection, meaning that it is like a pointer (under-the-hood) and thus, everytime you access the value of the referee, there is an implicit dereferencing happening (fetching the value at the underlying pointer address. This is the core of the issue, with a reference parameter, you eliminate the overhead of the copying (replacing it with the often cheaper operation of taking a reference (address) and copying that address (underlying pointer)), but then you suffer the overhead of the indirection every time you access the referee's value.

So, we can first take care of the case for T&, because that's easy. If you need to modify the call-site object that is given as a parameter, then you must use T& (unless you go with T*, however, that is usually not recommended).

If you need a fresh copy of the object within your function (e.g., to do some temporary modifications to it), then you should use T (pass-by-value). This is because if you are going to make an internal copy of the object (internal to the function-body), you might as well do it as it is being passed into the function because it is going to help the compiler avoid unnecessary copies from the call-site.

If …

triumphost commented: I chose my function parameters based on this answer :) +5
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I do R & D in the world of Metaphysics, and my specialised subject is Subtle Energies.

LOL! First time I hear about Research and Development in a branch of philosophy, let alone a non-empirical branch of philosophy. As for subtle energies, I bet they're subtle all right.

If you love random generation and metaphysics, you want to take a look at this random Deepak Chopra quote generator.

Normally in this field we have to use things like Pendulums, stick pads, muscle testing etc to obtain answers, not to mention intuition, none of which are good, because the mind can interfer with the result. I believe that a True Random Generator might be the answer.

I'm not sure what answers you are looking for, especially as you seem to want random answers. Anyways, you're right about one thing, the human mind is one of the worst source for random numbers or random answers, it's a provable fact that a human brain is completely incapable of coming up with a random choice (e.g., illusionists or "mentalists" use this fact a lot to perform their tricks). If you use just a basic pseudo-random number generator (which is basic standard function in just about every programming language that exists), then it will probably be totally sufficient for this purpose (at least, if your basis for comparison is a pendulum or a human mind!).

create lists of anything on my computer then randomly generate something from that …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

It depends what you mean by "share". If you just mean to "distribute" it, then that's just a matter of compiling your program in release mode (after being completely finished with it), packaging it in an installer, and distributing it (for free or for cash). If you mean sharing code with others (in the hopes that they will contribute to it), then you need to get them to sign an agreement (physically sign a paper, anything else is worthless) on the terms of their involvement, which can range from relinquishing all their copyrights to you, to giving you license to use their contributed code under specific clauses (e.g., it can be free of charge but closed source, or with a royalty, etc.). And, it goes without saying that if you are using code under a copyleft license (like GPL) then you don't have much choice, you must distribute your game as open-source, under the same license.

As for distribution (within an installer), then it is just a matter of picking one of the hundreds of sites that host freeware / shareware programs for download. Of course, you probably should first test that your program installs and works on many platforms (especially if in Windows). You can do that by asking friends or family that can give you access to a few different Windows computers with different versions and without any developer's libraries installed (like Visual Studio or whatever else), this is important to make sure you don't have dependencies. And …

claywin commented: Okay, thanks! That was a lot of helpful information; you answered my question and probably any future ones! +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

bump --> clump

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

supremacy -> necromancy

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

If you want better precision, you have to care about round-off error accumulation and amplification factors. Generally, things like subtracting numbers that are very close, or dividing number of wildly different magnitudes tend to make the round-off errors blow out of proportion. So, you can't really apply the mathematical formula verbatim and expect good precision all the time, you have to modify it to avoid ill-conditioned operations. I think that this sin function should work a bit better:

double MySin(double c /* in radians */, int Accuracy) {
    // you should clamp the c value within the first period around 0:
    static double pi = MyPi(1000);
    while(c > pi) c -= 2 * pi;
    while(c < -pi) c += 2 * pi;

    double result = c;
    int Minus = -1;
    double c_term = c;
    double c_sqr = c * c;
    for (int index = 3; index < Accuracy; index+=2 ) {
        c_term *= c_sqr / (index * (index - 1));
        result += Minus * c_term;
        Minus *= -1;  
    }
    return result;
}

And, of course, using the type double, you should never expect precision beyond 15 significant digits. For any precision higher than that, you will need to use a library for infinite-precision (variable-length) real number representations.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I wouldn't be surprised if Linus Torvalds (who used to program directly in machine code! (not assembly)) came out to say that Brain#### is a pretty good language for system programming!

BTW, I think there's a bug on line 4. ;)

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The benchmarks I have seen put fortran at about two or three times slower than C or C++, but that was on GCC compilers. So, unless you are using one of Intel's super-optimized fortran compilers, it's really not worth the trouble (and even then, I'm sure Intel's super-optimized C / C++ compilers are just as good if not better). I do interact with fortran code from time to time (in the field of numerical analysis, it's unavoidable), but I see it just as an annoying legacy language which is disgusting to look at and decypher.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Isn't any code written in FORTRAN horrible?

But this one is particularly horrible, probably the highest density of GO TO ever seen in one piece of code (excluding assembly code, of course).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Yay for me (it would have been nay a month ago, now I have grown to like it), but I like the toggle idea.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Just because current scientific knowledge doesn't know how to do it, doesn't mean someone someday won't figure out how to do it.

There is a difference between things that are believed to be impossible due to ignorance of the underlying physics, and things that can be proved to be impossible based on solid and well-tested scientific knowledge. People don't try to turn lead into gold anymore because now we understand to a very high degree of certainty what gold and lead are and how hard it would be to turn one into the other, for all practical matter, impossible. Hydrogen cars are pretty much in the same category, along with perpetual motion machines (do you think those are possible too? You never know, right?). Sure, there is always a possibility, however small, that there is some big breakthrough that flips all of known thermodynamics and particle physics on its head. But, applied science (a.k.a. engineering) is not in the business of trying to do things that are theoretically impossible while crossing fingers that some magical discovery is going to save the day.

The idea of a round Earth maybe wasn't main-stream in Columbus' time, but it had already been around for millenia, and I would imagine that anyone who had traveled a bit across latitudes (especially when navigating between Africa and northen Europe) would have had strong reasons to think the Earth was indeed round, and would have had a hard time navigating without knowing this, at least, …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Actually, I think it's more likely they will run on water. Hydrogen cell tech seems to be where the really big research and development investment is at.

Hydrogen cars are a red herring. I remember my first class in thermodynamics (back in my second year of my Mech. Eng. degree), as a basic introduction to thermodynamics analysis, the prof proved how idiotic it is to even attempt to use hydrogen to power cars. Even some engineers working on hydrogen cars concede that's it's all a big waste of time, but hay, if car companies pay people to waste their time, it's their problem, but don't buy the hype, because creating the hype is the point of it all.

By and large, the main issue with any kind of transportation energy source (fuel, hydrogen, gas, electricity) is light-weight energy storage systems (or energy-capacity versus system weight). Complex chemical fuels (like fossil fuels) provide incredibly high-density energy storage systems (about 12.5 kWh / kg), about 10 times better than hydrogen (compressed gas) (about 1 kWh / kg) and about 40 times better than current battery technology (about 0.1-0.3 kWh/kg). For hydrogen, it's pretty much pushed to the theoretical limit right now, so there isn't much hope (while liquid hydrogen is good for some purposes, it is not viable for transportation). For battery technology, there is some hope as there are infinite possibilities for electro-chemical storage, and there are electric cars with decent autonomy (of course, the batteries are still super …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

That would definitely be useful, it did happen to me quite a few times that I lost a fairly lengthy post because of a temporary loss of internet connection or by accidentally navigating away from the thread's page. It would be nice if there was an automatic draft saving, and if you come back to a thread's page (within a certain time period) it could tell you "you have drafted a reply earlier, would you like to recover it?". And also, to being able to click on a "save draft" button to keep it more permanent.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Well, here is a little code snippet I put together, enjoy!

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Why is Foo X(); wrong?

Because that is the declaration of a function that is called X, returns an object of type Foo, and takes no parameters. Because function prototypes can appear anywhere, including all places where variables can be declared, there is an ambiguity (is it a function? is it a variable?). The C++ standard resolves the issue by saying that if an ambiguity exists, then it should be interpreted as a function declaration. So, Foo X(); is a function declaration.

Why do I need the equal sign to declare a class like you did?

The equal-sign declaration just resolves the ambiguity in this case.

The compiler gives no error even with all options turned on.

That is a bit surprising, but I would guess your compiler bends the standard a bit on this issue (all compilers deviate from the standard a bit, especially with things that the compiler can accept, that wouldn't normally be accepted).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Hi all!

This is a simple piece of code that I wrote about a year ago, in response to a question on Daniweb. I decided to infuse it with some C++11 magic, which led to a pretty sweet code snippet, full of neat little techniques you might want to use, not to mention that this class can actually be pretty handy to have. There are a few similar implementations out-there, but they all use a different approach, my main inspiration is the scope-guard technique.

This is an implementation of a simple wrapper class template that allows a variable to be externally made thread-safe via a mutex lock. This can be useful when a single variable (e.g. non-trivial object, like a std::string or std::vector) is of a class that has no built-in thread-safety, when thread-safety is required. The typical solution is to pair the object with an associated mutex and lock it during any thread-sensitive operations. For example, if the object is a std::vector, one could wrap all calls that rely on the underlying elements (like accessing them or insertions) with a mutex lock. Although this is typically the best solution in terms of fine-grained control over when threads can get blocked, it can be cumbersome to implement in general (at worst, creating a "completely thread-safe std::vector" would involve wrapping every member function in a mutex lock).

This simple wrapper superposes a scoped locking mechanism on top of an underlying object. This forces all the operations on the object to be …

Ancient Dragon commented: good work :) +14
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Don't panic, relax, all is well.

What that tutorial is refering to is the difference between value-initialization, default-initialization, and zero-initialization. These are a set of rules that the compiler follows to initialize objects in different "construction from nothing" cases.

Basically, value-initialization and zero-initialization are the same, they set everything to zero (unless there is a user-defined default constructor for that class). This will occur when you write:

Foo f = Foo();   // value-initialized local variable.
     // btw: Foo f();  is an error, it is a function declaration!

Foo* pf = new Foo();  // value-initialized dynamically-allocated object.

Bar() : member_var() { };  // value-initialized data member.

While default-initialization will call the default constructor of the class if one exists (user-defined or not), otherwise it doesn't do any initialization. This will occur when you write:

Foo f;   // default-initialized local variable.

Foo* pf = new Foo;  // default-initialized dynamically-allocated object.

Bar() { };  // member_var will be default-initialized.

The same initialization rules apply to all kinds of initialization, whether it is a local (static) variable, a dynamically-allocated one, or a class data member (or a base-class initialization). There are only some special rules for global variable (with externs and stuff), but you don't have to worry about that.

At the end of the day, class types always use the default constructor (user-defined or not). The only "weird" thing about this is that primitive types (int, float, etc.) can be initialized to zero without having to do int i …

triumphost commented: Thank you! I feel much safer now. Going to read the PDF. :) +5
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

but what if question arise to reverse the link list without using pointers?

Well, a linked-list is just a chain of nodes pointing to each other. You can't really do anything with a linked-list without using pointers. To start, you have to use the "head" pointer, so that's already "using pointers". Or did I misunderstand your question? What do you mean by "without using pointers"?

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The difference between code 1 and 2 is that the code 2 is erroneous.

malloc() allocates memory of the given size and returns a pointer to that memory. If you allocate memory big enough to store one struct node then you would store the pointer in a struct node* pointer, as in code 1. So, in code 2, because the destination pointer is struct node*, the malloc function should be given sizeof(struct node). Or, if you use sizeof(struct node*), then the result should be stored in a struct node** pointer (i.e., pointer to a pointer to a node).

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Sorry about that, we tend to go astray when interesting issues are raised.

We can bring the conversation back down to your level (no offense), if you just give us more specific questions about what you would like clarifications on. And if you have code for this problem that you would like checked, please post it.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The result type of the function should be changed:

template <class T>
MinMaxVector<T>& MinMaxVector<T>::push_back(const T &d)
{
    v[sp++]=d;
    return *this;
}
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Well, you can look at it the other way around:

Programmer A: I have a problem, I would like to create an efficient stack-based dynamically-sized stack..
Programmer B: Well, that's a tough one... let me think.. you could use a VLA, but then you'd need to know the size in advance.. so, uhmm, euhh.. let me ask Dennis Ritchie.. wait, he passed away (RIP)... are you sure you need this? You know the stack can overflow, right?
Programmer A: Yes, and yes... Now that I think about it, I could build the stack using a recursion!
Programmer B: But... but... that's not a stack, that's a recursion!
Programmer A: What's the difference?
Programmer B: Euhh.. uhmm, nothing?
Programmer A: Exactly!
Programmer B: Ahh, the Force is strong in this one!
;)

Just another duality programmers have to live with. When you need recursion, you have to worry about the stack piling up (e.g., when implementing quick-sort). When you want to pile things up on the stack, you need to use recursion.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I assume you'll go with the "recursion is just an implicit stack" route?

Isn't that your opinion too, deceptikon? Once you are aware that a recursion stacks up memory on the stack (stack frames) (assuming no tail-call optimizations), then you see a recursion as just a way to implicitly create a stack on the stack. Unless you think recursions are magical, I don't see any other way to see them as. I would only question the "implicit" part, I would say "automatic" instead.

To me, using a recursion is only an elegant way of using a stack-based auxiliary storage, as in this:

// Recursive version of "Option 3" (by Schol-R-LEA):
void printReverseRecursive(struct Node* list)
{
    if (list)
    {
        printReverseRecursive(list->next);
        printf("%d ", list->data);
    };
};

// Iterative version of "Option 3":
void printReverseIterative(struct Node* list)
{
    if (!list)
        return;
    unsigned int list_size = 0;
    struct Node* current = list;
    while(current)               // --- get the size ---
    {
        current = current->next;
        ++list_size;
    };
    struct Node* node_stack[list_size];  // <-- VLA (C99)
    unsigned int i = list_size;
    for(current = list; current; current = current->next) 
        node_stack[--i] = current;       // <-- stack the node pointers.
    for(i = 0; i < list_size; ++i)       // --- reverse traversal ---
        printf("%d ", node_stack[i]->data);
};

Except for the obvious difference that it is much easier to grow a stack on the stack through recursion than it is via a variable-length array (or other stack-based dynamic allocation), and it is also more efficient. So, I don't see …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

What if I want to go from char* to BYTE*? I'd use reinterpret_cast right?

Yes and no at the same time. The reinterpret_cast is, generally, to be avoided because almost anything you do with it is "undefined behavior", except for this one case you pointed out. When casting pointers whose only common base is void* (i.e., the pointee types are completely unrelated), the expression reinterpret_cast<T*>(p) is required, by the standard, to be exactly equivalent to static_cast<T*>(static_cast<void*>(p)), which makes the reinterpret-cast just a shorter syntax for a double static-cast (cast up to a void-pointer and back down to a T-pointer). But, in almost all other cases, the C++ standard only requires that the reinterpret-cast be a reversible cast (and should only be used for that purpose), meaning that if you cast a A* pointer into a B* pointer and then cast it back to a A* pointer, then the A* pointer value should be the same. But, technically, the value of the intermediate B* pointer (the actual address it holds) is implementation-defined. In practice (not guaranteed by the standard, but often so in implementations), it will probably behave the same as one static-cast or as two static-cast (via a void-pointer, like in the case where the two types are completely unrelated), but there are cases where it might not (like cases where a static-cast would fail or be ambiguous, then, the output of the reinterpret-cast is really anybody's guess).

And, of course, when it comes to using reinterpret-cast for …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

is there ever a time when you would not want to define all three destructors as virtual?

Yes and no. Inheritance is mostly used in two contexts: for dynamic polymorphism in object-oriented programming; and for a number of template-heavy techniques in generic programming and template meta-programming. In the former case, it would be incorrect to not make the base-class destructors virtual (which makes all derived-class destructors virtual too!), this is one of the few hard rules about C++ good programming practices.

In the latter case, the classes involved in the inheritance are used to bury some implementation details (and bits of template-kung-fu), or often don't contain any data members and will never be instantiated (as an object), and in either cases, there is no need for virtual destructors because the inheritance hierarchy is not exposed (i.e., the user only sees and uses the top-level derived class, not its base-classes, for example, std::string and STL containers are like that (which explains the ugly, deeply nested error messages you get when you use them wrong, i.e., you catch a glimpse of the underlying class hierarchy)).

If you are using inheritance for other purposes, you are probably doing it wrong.

If b and c had no dynamic memory to delete, it would cause no problems to not call the destructors, but at the same time, what would the harm be?

Be careful with this. Do not confuse the lack of a user-defined destructor, with the lack of a destructor, …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Your question makes very little sense to me. What do you mean by "trying to get mangling for variable .."?

Name mangling is not something that you should ever be able to see from within the code, because it is a feature used for linking (happens after compilation).

Name mangling in C++ is necessary because of features such as namespaces and function overloading. When you compile a piece of code in C++, the compiler must generate a symbol table of all the functions, types and global (extern) variables that can be found in that code. This is used later when the linker puts many pieces of compiled code (object files) together to produce the final binary (library or executable). In C, this is trivially done by using the actual names of functions and variables as the names of the corresponding entries in the symbol table (i.e., no name mangling in C). But, in C++, because functions, types, or variables with the same name can exist (either as different overloaded versions or in different namespaces), the compiler has to create new names which encode the identifier (name of function, or variable), the namespace (or other scope), and the parameter types (for function overloads). This is called name mangling, and it generates a weird-looking and unique name for functions. The only reason to be concerned with that is if you are trying to inter-operate between C++ code and C code (or others), in which case, you must disable name mangling with the …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I have never tried this, but I believe that you can use this Wake-On-LAN feature that most ethernet cards have (unless it is really old, or bad). You can follow these instructions (they are a bit old, but should still work). You can install the package wakeonlan on your remote computer (not server) to do this, I believe you can even turn on a powered-off computer remotely with this program (I think that sending the WOL packet is basically equivalent to hitting the power-button). Of course, this assumes a wired connection between your server and a router. It's a pretty low-level thing (send a UDP packet to a MAC address), so I'm pretty sure you can also wake-up the server from a Windows / Mac computer too.

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

When it comes to corrupt pointers (as in, pointer that hold a non-NULL address, but the address points no-where (a freed memory, or otherwise)), there really isn't any mechanism to find out about it after the fact, except via a crash or other weird behaviors.

The name of the game here is prevention. You have to be disciplined and vigilant to make sure that you keep your pointers consistent.

Otherwise, if you do have problems for which you suspect that you have pointer problems, there are external tools you can use to find the problem(s). In particular, a good debugger should allow you to track pointer addresses as you step through the code, so, you can manually inspect those pointer values. Then, there are memory profiling tools, like Valgrind (under Unix-like environments) which is really good for that, I'm sure VC++ has nice tools too. A memory tool like that should detect "dangling" pointers (pointers that refer to memory that was freed), memory leaks (when you no longer have any pointer pointing to some memory that was not yet freed, which means you can never free it), and it will be much stricter about memory accesses and be able to trace the offending code (illegal access) back to the line of code that it originated from. This is makes the process of finding such errors much easier.

But again, the best remedy is prevention. Once you develop good discipline and techniques to deal with dynamic memory and pointers, those problems …

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster
// Selection sort.
//  'a' : A pointer to the first entry of the array of numbers to sort.
//  'n' : The number of entries in the array.
void selection_sort (int *a, int n) {
    int i, j, m, t;
    // For all positions 'i' in the array,
    for (i = 0; i < n; i++) {
        // find the position 'm' of the minimum value in the range 'i' to 'n' 
        for (j = i, m = i; j < n; j++) {
            // if the 'j' entry is lower than the current minimum 'm',
            if (a[j] < a[m])
                m = j;  // then mark 'j' as the current minimum 'm'.
        }
        // Swap the minimum entry with entry 'i', and move on to the next entry.
        t = a[i];
        a[i] = a[m];
        a[m] = t;
    }
}

I don't know what else to explain, there really isn't much to explain. Is there a specific thing that troubles you?

mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

One basic algorithm is basically just reversing the next-pointer between two adjacent nodes while traversing the list, and at the end, you swap the head and tail pointers. To do this, you need to keep three node pointers, for three consecutive nodes (the last may be NULL). If you have node1, node2, and node3, so that node1->next == node2 and node2->next == node3 in the original list, then you can reverse the order of the first two nodes with node2->next = node1;, and then proceed forward with node1 = node2; node2 = node3; and node3 = node2->next;, unless node3 was NULL, then you just finish.

I can't give more details if you don't show some efforts to do this on your own.