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

The Boost Graph libraries have an implementation of A* (and Dijkstra if you want) which both rely on a priority queue implementation that has more capabilities than the standard priority queue. Their priority queue implementation is also indirect which is also helpful. It supports functions like push_or_update, update, and contains which is really all you need for A*, which is lacking from the standard implementation. This queue is called d_ary_heap_indirect and here is a simple example by daviddoria on using it (among other nice examples of BGL). There is also a slightly more feature-rich and non-BGL priority-queue implementation available in the latest version of Boost called just d_ary_heap.

And btw, checking if a node is in the open list by checking if it is present in the priority queue is really inefficient. Most algorithms rely on a coloring scheme (or masking) meaning that you store, for each node, a status flag (or color) which tells you whether the node is open or closed (or un-discovered, or inconsistent, etc, depending on the variation of A* that you are implementing). It's very easy, as part of your algorithm, to make sure that the state of the flag is consistent with what set or priority-queue the node is in, because you control when nodes are moved in and out of the sets. I encourage you to either use the BGL implementation of A* or to at least check it out, see it here:

_Nemesis_ commented: Clear, concise response +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

English please.

$ sudo mount -t ntfs -o defaults /dev/sda5 /mnt/my_drive

where /dev/sda5 must be replaced by your hard-drives name, and /mnt/my_drive must be replace by whichever folder you which to mount the hard-drive into.

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

I'm an atheist, and have always been. My mother was born a catholic but was pushed away from it due to the horrible and discusting actions of the catholic church in the 50s, 60s and 70s, and once she left and stop being reinforced in the belief in God, the belief just disappears. My father was born into a very religious lutheran family, but he went on to study philosophy / sociology / psychology at the university level and soon realized that secular morality, ethics and humanism completely dwarf any moral "teachings" of the bible or any other ancient dogma, and once "religion as a source of morality" was clearly refuted (making God pretty much useless in human affairs) there was no point for him to stay in any such sectarian organisation. Personally, I've got the whole package: agnostic, atheist, and anti-theist.

Spinoza's God is not a literal one in any meaningful sense. In his philosophy of ethics, he used this term to appeal to a transcendental magnificence of the Universe. So, I guess that "believing in Spinoza's God" is really a question of whether one believes the universe to be transcendentally magnificent. From a philosophical stand-point it might be a comforting thought, but I don't see much purpose for this, even if it were true (no afterlife, no answering of prayers, no destiny for individuals (just for the cosmos), etc., it's not because there is somekind of order or plan to the universe that you are necessarily an important …

MooGeek commented: Thank you for your awesome reply! I was wanting to have an opinion with a Scientist and here it goes! Thank you so much! +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Using a forward-declaration. A forward-declaration is a way to declare a class or function before its full declaration or definition can be written. There are restrictions on how you can use the class or function before its actual declaration or definition appears. In case of a forward-declared class, because the compiler doesn't know what that class is, but only that the class exists (with the promise of a declaration to come), this means that you can only declare pointers or references to that class (i.e. no objects of that class) and you cannot address any of its members. Here is a typical FooBar example:

class Foo; // forward-declaration.

class Bar {
  public:
    void do_something(const Foo& f) const; // use the forward-declared class Foo, but only as a reference.
};

// now, provide the actual declaration of Foo:
class Foo {
  public:
    void do_something(const Bar& b) const;
};

//now that Foo is declared, you can define the member function of Bar:
void Bar::do_something(const Foo& f) const {
  f.do_something(*this); // call the member function of Foo.
};

Typically, you can use this trick for so-called circular dependencies, like in the above where the definition of Bar depends on the definition of Foo and vice versa. Usually, you put the forward-declaration and declaration of Bar in the header file for Bar (e.g. Bar.h), then you put the declaration of Foo in its own header file (e.g. Foo.h), and finally put the definition of the members of Bar in the source file for …

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

Does anyone like to use Linux over the Microsoft XP or System 7/8.

Everyone that I know who has ever tried to use Linux for any substantial amount of time (usually compelled to do so because of job, or just out of curiosity) and who is the least bit competent with computers (e.g. not mortally scared of a terminal window), they have all expressed strong preferrence for Linux (and I've never heard of someone familiar with Linux who preferred Windows). I'm no exception. One of the reasons for me is just the sheer difference I feel when I boot into Windows after having spent some time using Linux, I feel the weight of the Windows system crushing me:

  • it takes forever to boot up, and everything I do seems to take forever.
  • it constantly nags you for updates which are always tedious to install (next, next, I agree, next, next,... restart now!) (as opposed to almost always automatic and most of time reboot-free, under Linux).
  • the lack of integration (and inter-operability) of software packages makes it so that every time you have a little task to do, like converting video formats or reading a particular type of file, you have to either dig in dark corners of the internet for freeware tools for that particular job, or pull out your wallet for some big professional software that you don't really need. Either way, you end up with a system that is bloated with a huge amount of …
TrustyTony commented: Guru of rant! +12
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Line 38 should be outside of the while loop, as so:

        while (Student_ptr->next) {
            Student_ptr = Student_ptr->next;
        }
        Student_ptr->next = newStudent;

In other words, the loop iterates to the end of the linked-list and appends the new student at the end of the list. This error was the reason why the nodes were never added to the list.

And your display loop should have this condition instead:

while (Display_ptr) {

Otherwise, it will not display the last element.

What should the constructor of a linked list look like?

Let me just put your code into a simple class to illustrate how this is done. Notice that the code is essentially your code (with the above fixes):

#include "../../std_lib_facilities.h"

class Student {
public:

   string name;
   double gpa;
   Student *next;
};


// Here is a simple class for the linked-list:
class StudentList {
  private:
    Student* head; 

  public:
    // constructor: (same as initialization code at the start of your main function)
    StudentList() {
      head = NULL;   // start with an empty list.
    };

    // function to add a node (same as within your for-loop)
    Student* AppendNewStudent(const string& aName, double aGPA) {

      Student* newStudent = new Student;
      newStudent->name = aName;
      newStudent->gpa = aGPA;
      newStudent->next = NULL;

      if (!head) {
        head = newStudent;
      } else {
        Student* Student_ptr = head;
        while (Student_ptr->next) {
          Student_ptr = Student_ptr->next;
        }
        Student_ptr->next = newStudent;
      }

      return newStudent;
    };

    // function to display the entire list (same as the last part of your main function) …
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Your random generation function is wrong. First, you should not seed the random number generator every time you query for a number. You should only seed it once at the start of the main() function.

Also, your range computation in it is also wrong. You had this:

double ranDec(int start, int end, bool floater) {//by defult will return value between 0 and 1000
    srand (time(NULL)+rand());//creates random seed based off current time
    double num;
    if(!end) {//end=0 and or start=0
        num = rand()+start;
    } else {//no 0's passed or start=0
        num = (rand() % end + start);
    }
    if(floater) {
        num = num + (rand()/(static_cast<double>(RAND_MAX) + 1.0));
    }
    return num;
}

But you should have this:

double ranDec(int start, int end, bool floater) {//by defult will return value between 0 and 1000
    double num;
    if(end == start) { 
        num = rand() % 1000 + start;   // default of 1000 in range.
    } else {
        num = (rand() % (end - start) + start);  // this is correct.
    }
    if(floater) {  // ??? what is this?
        num = num + (rand()/(static_cast<double>(RAND_MAX) + 1.0));
    }
    return num;
}

Also, in your robot constructor, you should call the random numbers as so:

        this->x = (int)ranDec(1,screenX-2,false);  // this will always generate an in-bound value.
        this->y = (int)ranDec(1,screenY-2,false);

Other than that, I don't know what the crash is about, maybe you should investigate further (print out values of the variables around the point of crash) and identify the exact point of the crash. …

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

My professor asked me to search why this is happening

"searching" and "asking" is not the same thing. If he wants you to investigate the matter, then you should investigate the matter, not just ask someone else for the answer.

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

Let me elaborate:

  1. Boot with a live or rescue CD/DVD/USB drive.
    I think this is pretty straight forward, you find Live-CD from any distribution of Linux (or install CD), and when you boot from it, you don't go through installation (if available) but instead boot from the CD.

  2. Login as root.
    All LiveCDs will allow you to login as "root" (and refer to their website for the password, probably something like "root" or blank).

  3. Edit the drive partition table with the fdisk command (fdisk /dev/sda) and create a new linux partition.
    Once logged in, you open a terminal window (menu: System Tools -> Terminal). Then, you can run the command fdisk -l which will list the available hard-drives and partitions on the computer. Usually, the internal hard-drive will be called /dev/sda (and sdb, sdc, etc.). For each physical hard-drive, its partitions will be listed as /dev/sda1, /dev/sda2, etc. Now that you have this information, you can locate your harddrive and the partitions you wish to work with (delete, create, etc.). Note that information down (or copy-paste) for later reference. Then, you can launch fdisk /dev/sda to edit to partitions of the hard-drive /dev/sda (or whatever you identified as the correct hard-drive to edit). Then, you follow the on-screen instructions and possibly internet tutorials on using fdisk.

  4. Create a file system on that partition, such as ext3 or ext4, and give it a label such as "newdata-1".
    This is also part of the things you would do …

rubberman commented: Ah Mike, going back to the department of redundancy department are we? :-) kewl! +12
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

First part is simple, my name is mike. The second part is an enigma for you guys. 2000 taken to the power 17 is about the total mass of the observable universe measured in grams. No, actually 2000 bitshifted 17 times writes FA00000 in hex which is cool. I'm kidding, seriously, 2000 and 17 is the longitude and lattitude of this private island that I own. Muhuahaha, you'll never figure out what it means!

Also python (the animal) is spelled pyton in Finnish.

Yeah, when a finnish colleague of mine proposed to use "pyton" for a project we had, said in the finnish pronounciation, it took me a while to realize he was talking about Python. Minä rakastan Suomi, mutta se on mahdotonta oppia suomea!

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

If you have "learned" all those basic stuff that you listed, then it might be a good time to go through the FAQ Lite by Marshall Cline to fill in any holes that might remain (the devil is in the details) and to learn a number of basic coding guidelines.

As for books following that, Scott Meyers is surely a good source, although somewhat outdated. "Accelerated C++" by Barbara Moo and Andrew Koenig (who sporadically shows up on this forum) is also a fan-favorite with its less pedantic but more practical approach. From looking just at the table of contents, the new edition of "Professional C++" by Gregoire, Solter and Kleper, seems very nice, and would probably suite you very well if you are already quite familiar with the more basic syntax elements. Usually you can tell which C++ programming books are good and which are crap just by looking at the table of contents, if it doesn't start to delve into the STL containers and algorithms by the middle of the book at the latest, if it doesn't dedicate a good amount of pages to exceptions, and if it doesn't ever get to at least some basic introduction to templates, then it is crap, not really a book about C++.

Once past one or two of those books, the next set of books to aim for is the "C++ In-depth Series". The "C++ Coding Standards" by Herb Sutter and …

TrustyTony commented: Thanks for multiplying my post with real C++ knowledge ;) +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

First, you need to know that you are allowed to swap the values of variables. In other words, the variable a,b,c,d don't have to have the same value all throughout your program. So, the idea is not to print out the four variables in order, but to rearrange the values such that a < b < c < d at the end.

As an example, here is a start, the first if statement should be:

if( b < a ) {
  int tmp = a;
  a = b;
  b = tmp;     // at this point a stores the original value of b, and vice versa, the values are swapped.
};

After that if-statement, the first two values will be in order.

Then, you need an if-else statement to find out where c should go. If c should go at the start, then you need to "rotate" the values (a kind of double swap). If c should go between a and b, then you need to swap b and c. And if c should go after both a and b, then you don't need to do anything. This amounts to one if and one else-if statement.

Finally, you do the same for d. This time you'll one if and two else-if statements, which makes a total of 6 if-statements, as hinted to you.

It is also possible to proceed by putting c and d in correct order also (as with a and b), and then proceed to interleave the (a,b) …

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

I hope nobody asks you to sort 1000 values!

Ever heard of the concept of scalability?
Ever heard of arrays?
Ever heard of sorting algorithms?

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

Erasing elements while iterating through them is a problem because, with a container like std::vector, any operation that adds or erases elements in the vector will cause all iterators to be invalid. In other words, your iterator it that you use in your loop becomes invalid as soon as an element is erased (or added) from the vector. It needs to be reset. This is because erasing or adding an element to a vector might cause a reallocation of the memory, and since iterators for a vector is often just a pointer (or a thin-wrapper for a pointer), if the memory is reallocated elsewhere, that iterator no longer points to a valid element of the vector.

Not all STL containers are like that. There are different containers for different needs. Vectors are often not the best if you are going to do a lot of insertions / deletions in the middle of the sequence. The alternative, linked-lists which are more efficient at mid-sequence insertion / deletion, also have many drawbacks that are not negligible either (locality of references) and often out-weight its theoretical performance benefits.

Normally, the preferred strategy for removing elements of a vector is to simply not erase them just yet. Instead, you implicitely remove them from the vector by re-packing all the remaining elements, leaving a bunch of empty elements at the end of the vector, which you erase afterwards.

I think you can implement your algorithm simply by making clever use of std::remove (

TrustyTony commented: Thanks for your expert advice! Nice to see I was correct. +12
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

"If we gave this man an enema, we could bury him in a matchbox." - Christopher Hitchens (in layman terms: "this man is full of it")

On the geeky side:

"There are only 10 kinds of people in this world: those who know binary and those who don’t."

"Windows 95 is a 32 bit extension for a 16 bit patch to an 8 bit operating system originally coded for a 4 bit microprocessor by a 2 bit company that can't stand 1 bit of competition."

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

Since the overhaul, there are some nice feature of the old system that are missing in the thread titles. By thread titles, I mean as they show up in the forum listings (the icon (new, read, etc.), title, # of replies, time of last post, etc.). Some of the features of the old system that I have in mind are the following:

  • When hovering over the title, the first few lines of the question used to appear. Now, there's nothing.
  • The number of up-votes / down-votes associated to the OP used to appear on the right of the title. That seems to be gone.
  • What happened to the view-count on the thread?

I found that these features were pretty useful to determine if its worthwhile clicking and reading the thread. I miss them. Any plans for them to reappear?

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

Because the object is passed by value to the area function, meaning that another, temporary rectangle object is created and passed to the area function. When the area function is done, that temporary is destroyed. Afterwards, when the main function ends, the obj is destroyed.

Try changing the area function to pass by reference as so:

float area(const Rectangle& rect);

and similarly for the friend declaration of it. This should eliminate the temporary and result in only a single destruction.

Another test you can do is to create a copy-constructor as so:

Rectangle(const Rectangle& lhs) {cout << "Rectangle copy-constructed!" << endl; len = lhs.len; bre = lhs.bre; }
lastbencher commented: Thanks :) +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Don't even bother trying to give a circumvented explanation on the legitimacy of your purposes for this type of code. You're not talking to idiots who were born yesterday, don't insult our intelligence.

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

Here are some of my favorites:

"Hero­ism breaks its heart, and ide­al­ism its back, on the intran­si­gence of the cred­u­lous and the mediocre, manip­u­lated by the cyn­i­cal and the corrupt." - Christopher Hitchens (R.I.P.)

"You know you're a programmer if you always start counting from 0." - Geek saying

"When you smash the box, you have no choice but to think outside of it." - Me (in the context of describing generic programming techniques)

"Yes, inspiration exists, but it has to find you working." - Picasso

"As far as the laws of mathematics refer to reality, they are not certain; and as far as they are certain, they do not refer to reality." - Einstein

"Alles hat ein ende, nur die wurst hat zwei." - German saying ("Everything has an end, only sausages have two.")

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

Your algorithm is called "Selection Sort".

And no, it is not similar to heap sort. It is true that heap sort has a second phase where the max (or min) elements are extracted from the unsorted section and stacked at the beginning or end of the overall array. However, the main aspect of this phase is that the heap structure is always conserved. A heap structure is a way of arranging the elements of the array such that the max (or min) always appears in the first position in the array. If you set up the structure properly, you can efficiently remove the max (or min) element and update the heap structure to put the new max (or min) element at the first position. So, the heap sort algorithm first rearranges the elements of the array in a heap structure, and then it takes the max (or min), puts it at the last position in the array (before the start of the sorted section of the array), then it updates the heap structured section of the array, and repeats until the entire array is sorted. So, the second phase is similar to Selection Sort, but the important thing is that you don't have to look for the maximum element (which is linear-time) but instead maintain the heap structure (which is log-time, which is much faster).

You can explore heap sort by using the standard sort_heap algorithm, see the example on the reference page.

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

Ahh, good old bug-riddled GSL! If I had a dime for every bug in GSL, I'd be a rich man.

Obviously, the problem is that you run out of memory. Meaning you have a memory leak in a loop. If you call the above function within a loop without freeing the memory of both the rng and its state, you'll eventually run out of memory.

I hope that you understand how random number generators work and why you only need to seed it once. In theory at least, you shouldn't need to recreate a new random number generator more than once. I'm not familiar with GSL's rng code, but if it is logical, then it shouldn't require you to create the rng more than once, but again, GSL is not known for being logical or well programmed, quite to the contrary.

The real answer to your problem is to use the random number generators provided by the C++ standard <random> library or by Boost.Random. Which are both virtually identical and very feature-rich.

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

That's easy enough, all you need is to construct a predicate functor (and btw, that copying to temporary and turning the entire array into lower case is just incredibly wasteful).

What about this:

template <typename Predicate>
        bool Contains(Predicate pred) const
        {
            for (auto it = TypeData.cbegin(); it != TypeData.cend(); ++it)
            {
                if (pred(*it))  // evaluate predicate.
                    return true;
            }
            return false;
        }

        bool Contains(const T& ToFind) const // don't forget const!!!
        {
            return Contains([&ToFind](const T& rhs) { return (rhs == ToFind); });
        }

Now, all you need is a string version of this predicate that is case-insensitive.

First of all, when you get into the business of specializing templates for standard STL containers (like string) you should keep in mind that std::string or std::vector<T> or others are not as simple as they appear, because in reality they are, respectively, std::basic_string< C, CharTraits, Allocator> and std::vector<T, Allocator> . So, you should normally specify all template arguments in your specializations, otherwise you might get surprises later.

So, here is a simple predicate functor for case-insensitive comparisons:

// General template for non-strings.
template <typename T>
struct CaseInsensitiveCompare { 
  char cannot_create_a_case_insensitive_comparison_for_non_string_type[0]; // this will throw the compiler on a fit of rage!
};

// Specialize for strings:
template <typename C, typename CharT, typename Allocator>
struct CaseInsensitiveCompare< std::basic_string<C, CharT, Allocator> > {
  typedef std::basic_string<C, CharT, Allocator> argument_type;
  typedef bool result_type;
  
  argument_type lhs;
  CaseInsensitiveCompare(const argument_type& aLhs) : lhs(aLhs) {
    using std::tolower; //include standard tolower in ADL.
    for(auto it = lhs.begin(); it …
triumphost commented: I understand partially but thanks.. I'll look into it more to fully understand. +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

No, don't use goto for such a simple purpose. If nothing more, it's better to replace it with while(true) { }; loop (and a break; statement somewhere in the loop). At the least, this makes it clear that the code is a loop, and respect indentation rules, that alone is already a good argument to motivate using a traditional loop statement (for, while, do-while) instead of a label/goto pair.

Your code does a generation of permutations of letters of a word, may I suggest you take a look at std::next_permutation, and either get inspired from it or use it directly (hints: the number of permutation is fixed (implying a fixed-length loop); and you shouldn't need a random-number generator anywhere, the sequence of all permutations is something that is easily obtained systematically and is deterministic).

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

>> Any ideas on how I can get this program to actually run??

Reading the error messages and going to the line that they point to and take a look at them. Maybe it's just me, but I think this might be a good start to getting the program to run.

Generally, when code is posted on a forum like this, it gets posted to explain it as an example. The hope is that it will help people understand how to do it on their own.

If working code is all you care about, try this:

#include <cstring>
#include <algorithm>

const int STRING_BUFFER_SIZE=80;//increase this to allow for really long names
struct Individual {
    char firstName[STRING_BUFFER_SIZE];
    char lastName[STRING_BUFFER_SIZE];
    char cityName[STRING_BUFFER_SIZE];
};
bool isGreater(const Individual& a, const Individual& b) { 
  int result; 
  return ((result=std::strcmp(a.cityName, b.cityName))?(result>0):((result=std::strcmp(a.lastName,b.lastName))?(result>0):(std::strcmp(a.firstName, b.firstName)>0))); 
};
void bubbleSort(Individual* a, int aLength) { 
  for(bool swapped=true; (swapped && (!(swapped=false))); --aLength) 
    for(int i=1;i<aLength;++i) 
      if((isGreater(a[i-1],a[i])) && (swapped=true)) std::swap(a[i-1],a[i]); 
};

Try submitting this and watch your grade shrink! (or sky-rocket, if your teacher is a moron)

There's a big difference between exemplary (pseudo-)code, working code, and production code.

Labdabeta commented: That is the coolest implementation of bubble sort I have ever seen! It makes your point nicely. +4
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

>> When do I really need to use Delete in Destructors?

When your class is responsible for allocating the memory that a pointer points to, then it is also responsible for deallocating that memory. This is what is referred to as ownership.

If your class creates a dynamic array, or a dynamically allocated object, or an array of dynamically allocated arrays or objects, in other words, if you use 'new' in your constructor, then you must use delete in the destructor to deallocate that memory.

You can save yourself all this trouble by making use of "scope-based resource management" (or RAII for "Resource Acquisition Is Initialization). Classes like std::vector and std::string are examples of array-like RAII classes, and classes like std::shared_ptr and std::unique_ptr are examples of RAII classes that wrap a pointer (i.e. otherwise called a "smart-pointer").


>> I've been told every time I use new, it stays in memory even when out of scope and that I should use delete to remove it.. so do i need to do Delete P?

Yes. new is an operator to do a manual allocation of memory. If you allocate memory manually, you must deallocate it manually, with delete . Whatever piece of code allocates the memory, that piece of code (class, function, module, etc.) is responsible for deleting it in due time. Smart-pointers can be used to attach some automatic deallocation behaviour to the pointer, so that you don't worry about it down …

triumphost commented: AGAIN! Thank you and Lerner Both exactly what I wanted to know! +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

@mazzica

int ansPre=v2 + v2++ + v2++ ;//execute line then inc so res =8 + 8 +8

This is wrong. The postfix increment operator does not perform the increment after the line is completely executed. It performs the increment where it appears in the expression, however, the result of the expression is the original value.

I reiterate my equivalence code:

int post_increment(int& i) {
  int result = i;  //record the value of the variable _before_ incrementing it.
  i = i + 1;       //increment the variable 'i' by 1.
  return result;   //return the value recorded before the increment.
};

The expression:

int ansPre= v2 + v2++ + v2++;

is equivalent to:

int ansPre= v2 + post_increment(v2) + post_increment(v2);

Which, actually, has undefined behaviour due to the fact that there is no rule, in the standard, to prescribe the order by which the operands of an expression are evaluated. So, the possible results are (also note that the addition is right-to-left associative):

int ansPre = 8 + (8 + 9);    // (a)
int ansPre = 8 + (9 + 8);    // (b)
int ansPre = 10 + (8 + 9);   // (c)
int ansPre = 10 + (9 + 8);   // (d)

There is no telling what your compiler might produce as a result. This is the same reason why there is no way to tell what ( i == i++ ) will give, neither could you tell what ( i == ++i ) would yield. …

triumphost commented: Ahh I was confused until I read this =] Trick Exam question! Thanx :D +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The post-increment operator ++ will increment the variable by 1 and return the value of the variable _before_ the increment. In other words, the operation is semantically equivalent to:

int post_increment(int& i) {
  int result = i;  //record the value of the variable _before_ incrementing it.
  i = i + 1;       //increment the variable 'i' by 1.
  return result;   //return the value recorded before the increment.
};

In light of the above, it comes to no surprise that the first result prints out 3.

You could rewrite the code, in more details, as follows:

int ans = 0;
    int v2 = 8;
    int v1 = 5;

    //ans = v2 % v1++; .. is equivalent to:
    int v1_original = v1;  
    v1 = v1 + 1;   // v1 is now equal to 6.
    ans = v2 % v1_original;  // the modulus is taken with the original value of v1.

    cout<<(ans);                    //This prints out 3..

    // cout<<(v2 % v1++); .. is equivalent to:
    v1_original = v1;   // v1_original is now equal to 6.
    v1 = v1 + 1;        // v1 is now equal to 7.
    int ans2 = v2 % v1_original;  // modulus with original value.
    cout << ans2;  // This prints out 2..  ( 8 % 6 ).

    cin.get();
    return 0;

It is important to understand the difference between the post-increment and pre-increment operators. The preincrement ++i will return the value of the variable _after_ the increment. The post-increment i++ will return the value the variable had _before_ the …

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

>> 1. How do I check the Template's parameter type?

Using typeid is one way to do it, but a rather unusual one. It is more common to use one of the multiple type-traits that are provided by the <type_traits> library (C++11 only), and/or the ones provided by Boost.TypeTraits. As a simple example:

template <typename T>
T square_value(T value) {
  if( boost::is_complex< T >::value )
    return conj(value) * value;
  else
    return value * value;
};

The more useful type-traits include checking if the type has cv qualifiers, checking if it is convertible to some other type, to check if it is a pointer type, or reference type, etc... It is rather rare that you would want to check for one specific type, unless what you really want is to have a specialization (or overload in the case of function templates) for a given type.

Two other useful techniques are the BOOST_STATIC_ASSERT and BOOST_CONCEPT_ASSERT . In the first case, you can just check a condition at compile-time, like BOOST_STATIC_ASSERT(( typeid(T) == typeid(int) )); which will fail with a reasonably error message if the condition is not met. In the second case, you can assert that a given type satisfies a certain "concept", meaning that it can be used in certain ways, like BOOST_CONCEPT_ASSERT(( boost::CopyConstructibleConcept<T> )); to check if the type is copy-constructible for example.


>> 2. How do I restrict the Template's parameter to a specific type only?

There are …

jaskij commented: you sir, owned me +5
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

First things' first, to a large extent, classes in the C++ standard libraries (with the exception of iostreams) are not object-oriented classes, they are not meant to be derived from and they generally don't derive from other base classes from which you could also derive. The iostream library is an exception as it provides an OOP architecture with specific classes that are base-classes from which classes like std::ifstream or std::stringstream are derived, and these base-classes are indeed intended (possibly) to act as base-classes in user-side code (meaning you are "allowed" to derive from these classes). std::string , which is actually just a typedef for the class std::basic_string<char> , is not meant to be derived from or treated as a base-class in any typical OOP sense of the term. It does not (or at least not according to the C++ standard) have a virtual destructor or any other virtual functions for that matter. This means one thing: you will not get polymorphism from inheriting from this class. This is very important, because inheritance is (mostly) about dynamic polymorphism (polymorphism in the OOP sense). To a large extent, if inheritance does not give rise to a polymorphic use of your class, then inheritance is not necessary or useful, and should not be considered a good option.

Is it dangerous? Yes and no. If you assume that you can override existing functions of the string class or have polymorphic pointers to the class (when it actually points to the derived class), then …

WaltP commented: I stand corrected. Thanks. +17
StuXYZ commented: Superbly argue, thx +9
triumphost commented: YOU!!! Always giving me the best answer =] +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Once you understand the idea that the purpose of a programming language is to act as an interface between a human being and a computer, then it becomes pretty clear why goto statements are bad. A guy that is trained to write assembly code might have a brain that is closer to that of a computer than a human being.

Looking at the standard, except for the two small sections that explain what goto and label are, and the uses of goto to explain what under flow control constructs do, all the mentions of goto relate to the types of ill-formed code they can cause. This involves jumping across code that creates objects and jumping in or out of try-catch blocks. Another, more important issue is the non-linearity that they generally introduce in the code. A for-loop is easy to explain, run the statements from start to finish and do it again for X number of times. Code that is expressed as a number of loops and conditional statements is fairly easy to follow logically. A spaghetti code of goto statements, or careless jumping around is much harder to follow and thus much more error-prone (forgetting to handle a particular condition, losing track of values of variables, and so on).

It is true that a good programmer, especially one used to working with assembly code, will be able to mostly avoid those pitfalls when writing the code, but that is a very weak defense of gotos for one …

zeroliken commented: well said +9
Labdabeta commented: Nice Answer! +4
MrEARTHSHAcKER commented: Another great explanation +2
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

>>Can you please tell me what is meant by forward declaration?

A forward-declaration is just a way to tell the compiler that there will eventually be a class declared later in the code. It is only a means to stop the compiler from complaining that it doesn't recognize the type or class. Of course, this is only temporary, the actual declaration of the class has to appear eventually (if it is ever used). When there is only a forward-declaration available so far (as the compiler is looking through the code), you only can do very limited uses of the class in question. Because the compiler doesn't know what that class is, only that it is a valid class, you cannot use any of its member functions or anything, you cannot create an object of that class, you cannot have data members of that class or inherit from it, all you can do is declare pointer / reference parameters or data members to an object of that class. Forward-declarations are typically used when you have two classes that depend on each other (and in other more advanced cases). Here is a simple and typical pattern that involves a forward-declaration:

class Cat; // forward-declaration.

class Dog {
  public:
    void ChaseThisCat(Cat& someCat);  //use of forward-decl. to declare a reference parameter.
};

class Cat {
  public:
    void FleeFromThisDog(Dog& someDog); 
};

// Now, Cat has been declared and can be used to implement the member function of Dog:
void Dog::ChaseThisCat(Cat& someCat) {
  //... …
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Well, I guess I should answer this, considering that path-planning is the subject of my Ph.D. thesis (the scenario is quite a bit more complex, of course).


Storage:

What you refer to as a "navigation mesh" is essentially a graph (a network of nodes and edges that link them together). Associated with each node, you would normally have information like its position in the environment (2D, 3D, or more, in my research its 15 dimensions) and other information that is usually useful to determine the desirability of that position (like proximity to obstacles, proximity to some goal position, exposure to enemies to dangers, etc.). Associated with each edge, you would normally hold information like the traveled distance/time or other cost-of-travel values.

Now, once you understand that your navigation mesh is in fact a graph. You have to look into graph theory, which is a huge deal in computer science due to its many applications, namely, the internet! (which is also a graph or network). There are many kinds of graphs with different properties and different storage methods.

Generally, a path-planning problems over a fixed graph (or mesh) will involve a grid-like graph, which, in formal terms, is a planar and undirected graph.

In concrete terms, this also means that when looking for an actual implementation or method of storing your navigation mesh, you need to be looking for a "graph library". The most prominent and one of the best graph library …

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

Interesting question, it warrants a detailed response. So, let me address some issues before providing more concrete solutions.


>>I have started finding lots of uses for abstract classes to define interfaces for common functionality using polymorphism

RED FLAG!! Maybe you misspoke, because otherwise you are hinting at a very common mistake, what I call "derived class intersection" meaning that you take different concrete classes that your software already has (or that you have in mind) and you find things that they have in common (i.e. the intersection of the functionality sets) to lump them into some base-classes (as in, having base classes with the common functionalities). This is a problem for many reasons. Mostly, it is a problem with the design methodology that will have bad consequences going forward. The proper design methodology involves assessing the requirements that an algorithm or data structure imposes on the objects on which it must operate, and then, create abstractions that lump related requirements together as a base-class or interface or other things (see later). Then, you can create concrete (or final) classes that fulfill those requirements and thus, implement the abstraction / interface. The point is, the choice of functionality that should make up an interface should be solely motivated by how this object is intended to be used by the algorithms or data structures. What functionalities turn out to be common between different derived classes is irrelevant in the design of your base classes or interfaces. Down the …

Kanoisa commented: Very clear and descriptive, thanks +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Yeah, that's pretty much it. Try this for instance:

#include <pthread.h>
#include <iostream>

void* f(void*) {
  for(int i = 0; i < 1000; ++i) {
    std::cout << "Print from the Thread!" << std::endl;
    usleep(1000);
  };
  return NULL;
};

int main() {
  pthread_t threadID;
  pthread_create(&threadID, NULL, f, NULL);
  for(int i = 0; i < 1000; ++i) {
    std::cout << "Print from the Main Function!" << std::endl;
    usleep(1000);
  };
  pthread_join(threadID, NULL);
  return 0;
};

Compile with:

$ g++ pthread_example.cpp -o pthread_example -lpthread
wildplace commented: cool! i love your example! +3
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

It's actually simpler than you think (for once!). To do a partial template specialization in C++ you simply leave the unspecified template arguments in the template declaration and put them in the class name... well, that sounds weird, so let me just put it in code:

// This class-template has 2 arguments <T,Number>.
template<typename T, int Number>
class Data {            // This class-template is a general template 
                        //  because 'Data' appears unspecified (just 'Data').
    T Array[Number];
  public:
    T& operator[](int);
};
      
template <typename T,int Number>
T& Data<T,Number>::operator[](int element){
  return Array[element];
};


//this class template has only one argument <Number>
template <int Number>
class Data<double, Number> {    // This class-template is a specialization of 'Data' 
                                //  because 'Data' appears specified as 'Data<double,Number>'.
    double Array[Number];
  public:
    double& operator[](int);
};
      
// The member function of Data<double,Number> is simply as follows:
template <int Number>
double& Data<double,Number>::operator[](int element){
  return Array[element];
};

Once you understand the logic behind it, it's quite simple to understand. And the fact that template arguments are types or integral values, or even template template arguments, doesn't make much of a difference.

So, it's as simple as that keep the unspecified arguments in the template declaration ('template < .. >') and specify the other arguments in the class-name (while filling in the remaining arguments with the new template arguments).

You can actually do much more interesting things once you understand how this all works. For instance, these are also a valid partial specialization:

template <typename T, int Number>
class …
MrEARTHSHAcKER commented: Great effort to explain some advanced concepts :D +2
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Yeah sure, here's an example:

#include <boost/random/linear_congruential.hpp>

#include <boost/random/uniform_01.hpp>
#include <boost/random/normal_distribution.hpp>

#include <iostream>
#include <ctime>

int main() {
  boost::random::rand48 rng = boost::random::rand48((unsigned int)time(NULL));

  std::cout << " This is a random number: " << rng() << std::endl;

  boost::random::uniform_01<double> udist;

  std::cout << " This is a uniformly distributed 'double' value in range [0,1]: " << udist(rng) << std::endl;

  boost::random::normal_distribution<double> ndist(0.5, 0.25);

  std::cout << " This is a normally distributed 'double' value with mean 0.5 and std-dev 0.25: " << ndist(rng) << std::endl;

  return 0; 
};
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

The main problem you will have with STL's priority-queue is not the efficiency, but the fact that it provides no means to update the priority value of elements already in the queue. This is an operation that is required in A* (at least, the last time I checked).

If you take a look at the Boost.Graph Library's implementations of A* and Dijkstra, which both are just thin-wrappers for a Breadth-first visitation algorithm, use a custom priority-queue implementation. This priority-queue is called d_ary_heap_indirect which is a D-ary heap implementation with an indirect priority value, but most importantly, it includes the necessary update() and push_or_update() functions that STL's priority-queue lacks.

The STL's priority-queue will be faster for more simple operations (just pushing and popping) because it is a relaxed heap (giving constant-time pushing). But, for more dynamic operations, I recommend BGL's d_ary_heap_indirect implementation. Better, yet, you could just use the BGL's A* and Dijkstra implementations.

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

>>But I am not able to figure out from those discussions.

That's probably because of two reasons that make this question a bit hard to answer. Before getting into it, of course, the two standard options that are available are C standard functions (i.e. fread() and fwrite(), or fscanf() and fprintf() for formatted IO), and the C++ standard functionalities (the <iostream> library). Otherwise, there are always OS-specific functions. The main difference between the two standard options is the amount of buffering done under-the-hood (buffering means that either data is pre-emptively read in blocks and cached before you actually demand it, or written data is accumulated before being actually written to the file in larger blocks). The C++ iostream library is designed with buffering mechanisms at its core, and as a result, implementations of it (provided by your compiler-vendor) generally have more sophisticated, multi-level buffering under-the-hood (and on top of any buffering mechanism that may be implemented by the OS). On the other hand, the C functionalities are generally just thin-wrappers for OS calls (so the only buffering done is that of the OS).

Buffering is the cause of the first complicating reason why there is no definitive and generic answer to your question. The amount of buffering needed for good performance and how the buffering is implemented depends a lot on the way that you read / write data. By that, I mean, in some applications you keep a file open and write small chunks of data once …

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

There are a few possibilities:

The ugly way: Use variadic functions from C. This is what you were referring to with the "va_list". You should know that the first argument does not have to be the count of arguments, it could be anything, but C variadic functions don't give you any other means to know how many parameters were passed, so you need to figure out some way to infer that (e.g. the printf() functions infer it from parsing the formatted string). This method is ugly because it is not type-safe, and it has limited application.

The new way: Use variadic templates from C++11 (the new standard). The new C++ standard has introduced a new feature called variadic templates which allow for variable number of parameters to function templates (where each parameter's type is templated too). This is both type-safe and will tell you how many parameters have been passed. Of course, you have to do a bit of template wizardry to write the function (and its overloads for different specific parameters).

The traditional way: Use default parameter values. Basically, this allows for variable numbers of parameters, but the sequence is fixed, and has an upper-bound, of course. For most cases, this is sufficient.

The Boost way: Use the Boost.Parameter library. This library is designed to make "packs" with the parameters of the function, allowing any ordering, naming them explicitly at the call-site, and default values, of course. This is …

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

There are many problems with your code, here are some in-code comments:

class ANew
{
    private:
        vector<CustomType> ArrayOfTypes;
        int rows;

    public:
        ANew() {}
        ~ANew() {}

        CustomType& operator ()(int I)
        {
            return ArrayOfTypes[I];
        }

        CustomType& operator [](int I)
        {
            return ArrayOfTypes[I];
        }

        // You should not return a copy of the underlying vector, return a reference instead (notice the ampersand):
        operator const vector<CustomType>& () const
        {
            return ArrayOfTypes;
        }

        bool operator == (const ANew &AN) const // notice that this function should be const and should return a "bool" value.
        {
            return (ArrayOfTypes == AN.ArrayOfTypes);
        }

        bool operator != (const ANew &AN) const // ditto.
        {
            return !(ArrayOfTypes == AN.ArrayOfTypes);
        }

        // Don't use the = operator for doing an addition of an element to the array!
        //  Don't abuse operator overloading by redefining the semantics of the operator.
        //  The = operator means "assigning" not "inserting" or "adding an element".
        //  If you absolutely want an operator, use the << operator to add elements. 
        ANew& operator = (const CustomType& CT)         //Add the CustomType to ArrayOfTypes. Ex: ArrayOfTypes AN = CustomType X
        {
            //#define LEN(a) (sizeof(a)/sizeof(*a))
            // The LEN() macro doesn't make any sense.
            //  sizeof(this) will just be the size of a pointer on your computer. 
            //  sizeof(*this) is a fixed number (has nothing to do with the number of elements in the vector)
            // dividing those two gives a fixed and undefined value. 
            if (this[LEN(this)] != &CT)	 // here, you compare a pointer with an object!
            {
                this.push_back(CT); …
triumphost commented: TY =] Triumphost. +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

C is generally considered as a low-level to mid-level, functional programming language which moderately enforces an explicit and static type system. It provides native syntactic support for structured and procedural programming paradigms.

C++ is generally considered as a general-purpose (low- to high-level), multi-paradigm programming language which strongly enforces an explicit and static type system. It provides native syntactic support for many programming paradigms: structured, procedural, object-oriented, generic and pure-functional meta-programming.

The relation between C and C++ is simply a matter that the basic syntax is essentially the same, and that, a few compatibility issues aside, the C language is a sub-set of C++ (i.e. C code can generally be compiled with a C++ compiler, although there are some incompatibilities that are generally minor issues that are easily resolved).

Object-oriented programming is a programming paradigm (i.e. a way to think of and create a software design). The basic idea is to associate data and functions into one coherent entity (a "class", which, once instantiated, creates an "object"). But usually, when people talk of object-oriented programming (OOP), they also include, at least, the use of three fundamental features: encapsulation, abstraction and polymorphism. C++ is not a pure object-oriented programming language, because (a) OOP is only one of the programming paradigms C++ provides features for, and (b) real …

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

Line 15 is an implicit conversion operator. The return type is const ANew* , and so, you need to return that. As in:

operator const ANew* () const
        {
            return this;
        }

But I have to say that I don't approve of this implicit conversion from value to pointer, it is dangerous.

As for the call-operator, you need to the correct return-type:

CustomType& operator ()(int I)
        {
            return ArrayOfTypes[I];
        }
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

If you use the Boost.Random library, there are plenty of different random number generators with all sorts of cycle lengths (including 2300000 bits).

The new C++ standard (C++11) also includes a random library with a lot of the generators from the Boost.Random library, but you need a fairly recent compiler to have that available. You can also use the underlying random number engines to create custom generators, the methods available are linear-congruential, mersenne-twister, subtract-with-carry, discard-block, independent-bits, and shuffle-order engines. There are plenty of options.

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

Here's my take on this. I mostly agree with most posters so far. Just adding my grain of salt.

Myth #1: " const variables are less efficient than #define s"
First, all const-variables are not the same. In this case, we are talking only about const-variables which could be (easily) replaced by a #define, so that limits things to POD-types (in the formal sense), meaning you won't have pesky little hidden mutables. Also, we have to make a distinction between static scope and an extern scope. A const-variable with static scope (which is the default, btw) will, by definition, always carry the value that it is given at the declaration-site. That's not true for extern const-variables because their values (or references to it) are resolved by the linker. So, in the case of a static const-variable, there is no reason why the compiler cannot replace the occurrences of the const-variable with its literal value, because that is well within the purview of the "as if" rule for code optimizations by the compiler, and, in fact, unless there are debugger-symbol requirements involved, most decent compilers will perform this optimization.

Second point is, as some have said already, the actual performance difference (if any) will be negligible compared to the macro-scoping issues like the logic of the algorithms, locality of reference, etc..


So, now that we know that efficiency-wise there won't be a noticeable difference between your 3 options, or at least, not enough to care …

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

There are some limits to what you can do with a forward-declared type. In technical terms, a type that was forward-declared but not yet declared (the compiler has not reached the full declaration yet) is called an "incomplete type". The reason for that name is simple, the compiler doesn't have complete knowledge of what that type is, it only knows that it exists (or will exist in the future). So, you cannot write code that uses the class in any way, but, fortunately, you can use pointers or references to the class (but you cannot dereference them). In other words:

class MyClass; //forward-declaration

void f1( MyClass c ); // ERROR: incomplete type.

void f2( MyClass* p ); // OK!

void f3( MyClass& r ); // OK!

void f4( MyClass* p ) {
  p->someMember();   // ERROR: incomplete type.
};

Get it? The point is that the compiler can figure out what a pointer or reference looks like (in binary) without having complete knowledge of what it points to, all the compiler requires is that the pointer or reference points to an object of a valid class or type, and the purpose of the forward-declaration is to temporarily satisfy that requirement until the actual declaration appears.

So, when it comes to the code you posted, you can fix the compilation error by doing this:

struct Point
{
  //...
    Point MidPointBox (const Box& B);  // make it a reference.
  //...
};

struct Box
{
  //...
};

// then, put the …
triumphost commented: Thanks! +6
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

You cannot separate the compilation of the function template from its declaration. Function templates are only compiled when they are used with a specific template argument, we call that instantiation. Your implementation of the function is never compiled in the cpp file because it is not used when you compile that cpp file, so the linker doesn't find it when you use it from another piece of code (translation unit).

The solution is that the implementation of that function template has to appear in the header file in which it is declared.

Read more about it here.

Allander commented: Quick and helpful reply. Many thanks! +0
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Of course, you can use malloc() but you have to be aware that it doesn't call the constructor, so, you have to do that yourself directly afterwards, through a placement-new operator.

The "initialize" function that you posted:

//create and initialize
void* initialize()
{
  MessageQueryTester* self = (MessageQueryTester*)malloc(sizeof(MessageQueryTester));
    memset(self, 0, sizeof(MessageQueryTester));
 
 return self;
}

is a repugnant piece of junk code.

You ask, is this safe? Hell no! You proved that yourself by causing it to crash when changing the container to a list instead of a vector. My guess is that setting all bytes to 0 of a vector object does not cause a crash, but that's just luck (probably, a vector only stores a pointer to the data and a size-value, and if you set both to zero it is an OK state for the object). And that is exactly how that code is expected to function: cause a later crash in most cases, go unnoticed on a few lucky(?) occasions.

There are two terrible errors in the code, (1) it uses memset to initialize the memory of the object which is completely wrong, and (2) it strips the type information out of the pointer it returns (and does a cast on that occasion, which has undefined behavior).

There are two correct options:
1) Use malloc and placement-new:

//create and initialize
MessageQueryTester* initialize()
{
  MessageQueryTester* self = (MessageQueryTester*)malloc(sizeof(MessageQueryTester));
  new(self) MessageQueryTester();
  
  return self;
};

2) Just use the regular new operator:

//create …
Ancient Dragon commented: great stuff :) +17
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

I guess so, broadly speaking.

However, a game engine would usually be more generic, as opposed to specific. I would be reluctant to say that any computer game is also a game engine. Like a car engine, you provide the decorations (exterior and interior) and the engine drives it. I like to think of game engines in the same way. Most game engines are built such that you provide it with some code for the game logic (specific to the game you want to do), some models, sounds and images and stuff, and the engine makes all this come to life. So, in general, a game engine is just a set of tools (for programmers) that are integrated together and perform all the generic tasks that any game (within a certain category of games) would need, such that you only have to make cosmetic changes to create a new game.

The kind of things a game engine would do might include: rendering a 2D/3D environment, processing user-inputs, simulating physics, managing the game-data (models, images, sound effects, etc.), managing network play, etc. Of course, the level of genericity will change from one engine to another but most will allow quite a lot of customising.

So, of course, if you have software for one game, and it is reasonably well programmed, that you can certainly consider that the more central piece of it is a "game engine" for that very specific kind of game you did. But the design …

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

>>If so, to apply more than once visitor which each computes a different difference, would you do something like this:

No offense, but that's a terrible idea. Not necessarily dangerous, but inefficient and ineffective.

If you need to do many things with each pixel / pixel-pair / patch / patch-pair, then make a composite visitor, which is, in turn, a visitor as well. It's that simple. Basically, a composite visitor would just be a visitor class that is also a container of visitors. For each element(-pair) that it is given, it traverses all the visitors that it contains and applies them to the element(-pair). You can also have special composite visitors with a special set of visitors, the possibilities are endless. So, your example would be more like this:

vector<PixelPairVisitor> visitorsToApply;
CompositeVisitor all_visitors(visitorsToApply);

image.VisitAllPatches(all_visitors)

// Extract the accumulated values from the "visitorsToApply" vector (or the composite visitor).

This is more effective because you can treat any kind of visitor just the same, in other words, it scales much better to the development of various kinds of visitors. To achieve this, you have to realize that you CANNOT channel the "output" of the visitors through the image-traversal function (the "VisitAllPatches" function for example), because this will almost inevitably cause you to make an assumption about the nature of the output of the visitor (unless you have some really good template meta-programming kung-fu skills under your belt). Don't make assumptions that restrict the applicability of your methods unless there …

daviddoria commented: Again, thanks! +12
mike_2000_17 2,669 21st Century Viking Team Colleague Featured Poster

Wow, this is a bit messy, to say the least.

Your problem is with the mixed responsibilities more than any other issue (templates, non-templates, base-classes, etc., these are all just details). From my understanding, the overall purpose is to perform transformations which are characterised by the following:
- Input
- Output
- Map
- Iterator
- Fold

For example, taking the difference between two patches means that you need a way to iterate through the pixels of the patches, then map the two pixel values to some difference value, and finally fold all those differences somehow to obtain a single output that represents the overall difference between the patches.

Your SelfPatchCompare thing has a similar pattern, it takes a patch and an image as input, needs a way to iterate through the patches of the image, then map the patch-pairs to some difference value (patch-difference), and then fold those differences into a single overall difference value.

What I'm trying to emphasise here is that you shouldn't be asking yourself where to put what data or which inheritance-scheme makes more sense, but you should ask what is the functional skeleton of your problem and where are its knots (or joints, or atomic elements). This is how you get to modularity, and once that is done, static vs. dynamic or how to shape the inheritance tree are usually self-evident and trivial to solve with a little programming know-how.


One concrete …

daviddoria commented: Mike has gone above and beyond yet again! +12