-1

I am quite new to C++ and I am experiencing some of its features. In particular, I would like to implement a generic Divide and Conquer procedure that takes as parameter the 'functional code' to solve the problem. Actually what I have in mind is that 'Divide and Conquer' is a higher order function taking as parameters:

  • a function basecase() to test whether the we reached the base case
  • a function solvecase() to solve the base case
  • a function divide() to split the problem in two subproblems
  • a function conquer() that takes to partial solutions and builds one solution out of the two.

To do this, I used templates: in particular, type A is the type of the input problem, type B is the type of the output problem and type C will probably be a boolean (the result type of the test for base case).

There is also other stuff, like the use of async() to compute concurrently the solution, but this is not the focus of my question.

My question is: why do I get the following message when I compile, complaining about conflicting types ?

D&C.cpp: In function ‘int main(int, char**)’:
D&C.cpp:67:92: error: no matching function for call to ‘divideAndConquer(std::string&, main(int, char**)::__lambda4&, main(int, char**)::__lambda5&, main(int, char**)::__lambda6&, main(int, char**)::__lambda7&)’
     divideAndConquer(text, lambda_basecase, lambda_divide, lambda_conquer, lambda_solvecase);
                                                                                            ^
D&C.cpp:67:92: note: candidate is:
D&C.cpp:10:3: note: template<class A, class B, class C> B divideAndConquer(A, C, A, B, B)
 B divideAndConquer(const A x, C basecase, A divide, B conquer, B solvecase)
   ^
D&C.cpp:10:3: note:   template argument deduction/substitution failed:
D&C.cpp:67:92: note:   deduced conflicting types for parameter ‘A’ (‘std::basic_string<char>’ and ‘main(int, char**)::__lambda5’)
     divideAndConquer(text, lambda_basecase, lambda_divide, lambda_conquer, lambda_solvecase);

The Code is the following: basically the idea is to use the Divide and Conquer procedure to look for the instances of a target string into a text, but to be general enough to reuse this schema to solve also other problems (e.g. the Fibonacci numbers).

#include <iostream>
#include <thread>
#include <string>
#include <fstream>
#include <future>

using namespace std;

template <typename A, typename B, typename C>
B divideAndConquer(const A x, C basecase, A divide, B conquer, B solvecase)
{
    if (basecase(x))
    return solvecase(x);

    A probl1 = divide(x,1);
    A probl2 = divide(x, 2);

    auto sol1f = async(divideAndConquer, probl1, basecase, divide, conquer, solvecase);
    B sol2 = divideAndConquer(probl2, basecase, divide, conquer, solvecase);
    B sol1 = sol1f.get();

    return (conquer(sol1, sol2));
}

int splitOnWhitespaces(const string& text, int half, string direction){
    int i=half, N = text.length();
    if (direction.compare("forward")==0){    
        while(text[i]!=' ' && text[i] != '\n' && i < N)
            i++;
    }
    else{
         while(text[i]!=' ' && text[i] != '\n' && i >= 0)
            i--;
    if (i<=0 || i==N)
        return -1;
    else
        return i;
    }
}

int main(int argc, char *argv[]){
    string text= "Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura, che la diritta via era smarrita.";
    string target = "nel";
    int base = 10;
    auto lambda_basecase = [base](string text){return text.length()<=base;};
    auto lambda_divide= [](string text, int subprobl){
         int half = text.length()/2;
         half = splitOnWhitespaces(text, half, "forward");
         if (half==-1)
            half = splitOnWhitespaces(text, half, "backward");
         if (half==-1)
             throw runtime_error("Error splitting the string: no whitespaces\n");
         return ((subprobl==1) ? string(text, 0, half) : string(text, half));
     };
    auto lambda_conquer = [](int count1, int count2){return count1+count2;};    
    auto lambda_solvecase = [target](string baseText){
        int found = 0;
        int start = 0;
        int len = target.length();
        while(baseText.find(target, start)!=string::npos ){
            found++;
            int position = baseText.find(target, start);
            start = position + len + 1;
        }
        return found;
    };
    divideAndConquer(text, lambda_basecase, lambda_divide, lambda_conquer, lambda_solvecase);
    return 0;
}

I am studying template programming right now, so any help about this issue and any reference to (good) books or webpages explaining the matter would be appreciated. Thank you in advance!

max66
  • 65,235
  • 10
  • 71
  • 111
Francesco
  • 29
  • 4
  • Did you mean to write `const std::string x` instead of `const A x`? Otherwise the types of parameters don't match (If so, we should close as typo). btw `const std::string& x` might be a better choice. – AndyG Nov 15 '17 at 16:43
  • There are many typographical errors here, so I'm voting to close. You seem to get your template types mixed up with other types. At the same time, I want to help you without posting an answer, so I've mocked up something that will *compile* [here](https://wandbox.org/permlink/N24l4R9dFmchvQMw). Note, you likely have logic errors, too. – AndyG Nov 15 '17 at 16:49
  • 1
    You expect 1st and 3rd argument of divideAndConquer function to share the type (except from const), but passing std::string and lambda. Those have different types, resulting in lack of match. – nowaqq Nov 15 '17 at 16:55

1 Answers1

0

First of all, a suggestion: give names to template type that are more useful (that suggest the use) than A, B, C, ...

So become more understandable that you have given the same type identifier to completely different things (A for std::string and for a lambda function; B for two different lambda types (and take in count that different lambda are ever different types; also when with the same signature) and for the return value (int, in your example)).

I propose the following version

template <typename dataT, typename divT, typename conqT, typename baseT,
          typename solvT>
auto divideAndConquer (dataT const & x, baseT basecase, divT divide,
                       conqT conquer, solvT solvecase)
   -> decltype(solvecase(x))
 {
   using retT = decltype(solvecase(x));

   if ( basecase(x) )
      return solvecase(x);

   dataT const probl1 { divide(x, 1) };
   dataT const probl2 { divide(x, 2) };

   auto sol1f = async<retT(*)(dataT const &, baseT, divT, conqT, solvT)>
      (divideAndConquer, probl1, basecase, divide, conquer, solvecase);

   retT sol2 { divideAndConquer(probl2, basecase, divide, conquer,
                                solvecase) };
   retT sol1 { sol1f.get() };

   return conquer(sol1, sol2);
 }
max66
  • 65,235
  • 10
  • 71
  • 111