tags:

views:

187

answers:

4

Hello all :)

Working on an algorithm to look at a STL container of STL strings (or other strings, making it general)

Basically it loops through something like a std::list and returns the length of the longest beginning in common. It's for processing lists of files, like this:

C:\Windows\System32\Stuff.exe
C:\Windows\Things\InHere.txt
C:\Windows\Foo\Bar.txt

This should return 11, because "C:\Windows\" is in common.

Never written a templatized function before, and my compiler is complaining. Here's my code:
Header:

// longestBegin.h -- Longest beginning subsequence solver
template <typename SequenceSequenceT, typename SequenceT, typename T >
size_t longestBegin(InputIterator firstCandidates, InputIterator lastCandidates);


Implementation:

// longestBegin.cpp -- Longest beginning subsequence solver
#include <stdafx.h>

template <typename SequenceSequenceT, typename SequenceT, typename T >
size_t longestBegin(InputIterator firstCandidates, InputIterator lastCandidates)
{
    SequenceT firstString = *firstCandidates;
    size_t longestValue = firstString.length();
    firstCandidates++;
    for(size_t idx = 0; idx < longestValue; idx++)
    {
     T curChar = firstString[idx];
     for(InputIterator curCandidate = firstCandidates;curCandidate != lastCandidates; curCandidate++)
     {
      if ((*curCandidate)[idx] != curChar)
       return idx - 1;
     }
    }
    return longestValue;
}

I have a funny feeling I'm missing something fundamental here......

The compiler bombs with the following error:

error C2998: 'size_t longestBegin' : cannot be a template definition

Any ideas? Thanks!

Billy3

+2  A: 

You can't forward declare templates, I believe. Try moving the implementation into the header file.

kitchen
+1  A: 

It looks like you'll first need to put the implementation into the header file since it's a template function and only "implemented" at compile time.

Alan
Not ripping on your answer. However "kitchen" was faster ;)
Billy ONeal
+1  A: 

InputIterator is not a type I think. Can you declare

InputIterator x;

in your code or you get an error?

KIV
That is true, but not the cause of the failure I'm questioning.
Billy ONeal
+4  A: 

Your parameter names in the template line need to include any types of function parameters or return types. This means that you need to mention InputIterator in your template parameter list. Try changing your function declaration to:


template <typename InputIterator>
size_t longestBegin(InputIterator firstCandidates, InputIterator lastCandidates)

Your next problem is: how does the compiler know what SequenceT is? The answer is that it's the result of dereferencing an InputIterator. Iterators that aren't pointers have a nested typedef called reference, which is just what you need here. Add this to the start of your function so the compiler knows what SequenceT is:


template <typename InputIterator>
size_t longestBegin(InputIterator firstCandidates, InputIterator lastCandidates)
{
    typedef typename InputIterator::reference SequenceT;
[etc.]

You could have kept SequenceT as a template parameter, but then the compiler couldn't guess what it is from looking at the arguments, and you'd have to call your function by typing e.g. longestBegin<string>(arguments), which isn't necessary here.

Also, you'll notice that this doesn't work if InputIterator is a pointer -- pointers don't have nested typedefs. So you can use a special struct called std::iterator_traits from the <iterator> standard header that can sort out these problems for you:


//(At the top of your file)
#include <iterator>

template <typename InputIterator>
size_t longestBegin(InputIterator firstCandidates, InputIterator lastCandidates)
{
    typedef typename std::iterator_traits<InputIterator>::reference SequenceT;
[etc.]

Finally, unless the first string is always the longest, you could end up accessing a string past the end of its array inside the second for loop. You can check the length of the string before you access it:


//(At the top of your file)
#include <iterator>

template <typename InputIterator>
size_t longestBegin(InputIterator firstCandidates, InputIterator lastCandidates)
{
    typedef typename std::iterator_traits<InputIterator>::reference SequenceT;
    SequenceT firstString = *firstCandidates;
    size_t longestValue = firstString.length();
    firstCandidates++;
    for(size_t idx = 0; idx < longestValue; idx++)
    {
        T curChar = firstString[idx];
        for(InputIterator curCandidate = firstCandidates;curCandidate != lastCandidates; curCandidate++)
        {
                if (curCandidate->size() >= idx || (*curCandidate)[idx] != curChar)
                        return idx - 1;
        }
    }
    return longestValue;
}

Also note that the function returns (size_t)(-1) if there is no common prefix.

Doug
Yeah.. there are problems with the logic of that code... it was slap together to see how the syntax worked. Thank you though :)
Billy ONeal
Checkmarked for the most complete answer. Thank you!
Billy ONeal
@Doug: I believe your answer miss some lines, the template declaration list is unomplete. I guess you are interested to know that , don"t you? ;-)
yves Baumes
@yves: thanks, fixed
Doug