2
// Example program
#include <iostream>
#include <string>
#include <set>
#include <map>
#include <algorithm>


struct cmp {
   bool operator()(std::string i, const std::pair<std::string,std::string>& p) const
   {
     return i < p.first;
   }

   bool operator()(const std::pair<std::string, std::string>& p, std::string i) const
   {
     return p.first < i;
   }
};

int main(){

     std::set<std::string> s1 {"--name", "--id"};      //Conditionally defined mandatory parameters
     std::map<std::string, std::string> s2 { {"--name","Admin"}, {"--group","Group1"}};      //options given by user

     std::set<std::string> result;

     std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(),
     std::inserter(result, result.end()), cmp());

     std::cout << *result.begin();
}

I'd want the output to be such that if user has missed any one of the mandatory parameters it should throw error, at the same time if user has mentioned any extra parameter other than the mandatory args an error is again expected.

Also set_symmetric_difference fails to work with it.

CMouse
  • 130
  • 3
  • 19

2 Answers2

2

It needs to be able to compare any combination of elements that you give it, including string to string and pair to pair.

struct cmp {
    bool operator()(const std::string &i1, const std::string &i2)
    {
        return i1 < i2;
    }

    bool operator()(const std::pair<std::string, std::string> &p1, const std::pair<std::string, std::string> &p2)
    {
        return p1.first < p2.first;
    }

    bool operator()(const std::string &i, const std::pair<std::string, std::string> &p)
    {
        return i < p.first;
    }

    bool operator()(const std::pair<std::string, std::string> &p, const std::string &i)
    {
        return p.first < i;
    }
};

output:

--id

From the documentation http://en.cppreference.com/w/cpp/algorithm/set_difference

The signature of the comparison function should be equivalent to the following:

bool cmp(const Type1 &a, const Type2 &b);

The signature does not need to have const &, but the function object must not modify the objects passed to it. The types Type1 and Type2 must be such that objects of types InputIt1 and InputIt2 can be dereferenced and then implicitly converted to both Type1 and Type2. ​

I tried it with just that one comparison function, and it doesn't work. If I comment out any one of the functions, it doesn't work. That last line appears to mean that both iterators must be implicitly convertable to both types, if the compiler/std implementation I tested on are right. But I'd rather just provide all four combinations.

Community
  • 1
  • 1
Kenny Ostrom
  • 5,639
  • 2
  • 21
  • 30
  • Oh yes, I missed it ! Meanwhile can you hint me a possible approach for the problem mentioned ? – CMouse Apr 21 '17 at 05:36
  • Still the `set_symmetric_difference` does not give the output expected, as the behavior of this function is taken, it should return all the options those are not in both, set as well as map so here in question it should be `--id `and `--group` – CMouse Apr 21 '17 at 05:40
  • I doesn't seem to compile, can you please provide with `set_symmetric_difference` a code snippet ? – CMouse Apr 21 '17 at 09:36
  • The types from s1 and s2's iterators are incompatible. That's not a problem when we give set_difference a cmp which can handle all the types, but this won't work with set_symmetric_difference, because that will try to plug values from s2 into result. You'd have to change the type returned by s2's iterator, which is already done with boost in another answer, and can also be done in c++14. However, the easiest solution is to just make a set from the map keys, and use that. – Kenny Ostrom Apr 21 '17 at 19:40
1

Your code as written worked on my computer and on IDEOne.

I looked at the C++ specification working draft, and although I don't see requirements on the user-supplied comparator that would make the behavior undefined, it does seem strange to me that the comparator that is passed to set_difference is of a different type than the Compare template type parameter to std::map and std::set.

If I were approaching this problem, I would use a Boost transform_iterator to map the user-supplied options map iterator to the std::string keys (option names). See Iterate keys in a C++ map. I would then use the default comparator, std::less:

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <utility>

#include <boost/iterator/transform_iterator.hpp>

int main(){
  std::set<std::string> standard_option_names {"--name", "--id"};
  std::map<std::string, std::string> user_supplied_options { {"--name", "Admin"}, {"--group", "Group1"} };

  std::set<std::string> different_option_names;

  auto get_option_name = [](const std::pair<const std::string, std::string>& p) -> const std::string& {
    return p.first;
  };

  std::set_symmetric_difference(
      standard_option_names.cbegin(), standard_option_names.cend(),
      boost::make_transform_iterator(user_supplied_options.cbegin(), get_option_name), boost::make_transform_iterator(user_supplied_options.cend(), get_option_name),
      std::inserter(different_option_names, different_option_names.end()));

  for (const std::string& different_option_name : different_option_names) {
    std::cout << different_option_name;
    if (standard_option_names.find(different_option_name) == standard_option_names.end()) {
      std::cout << " (unknown option)";
    } else {
      assert(user_supplied_options.find(different_option_name) == user_supplied_options.end());
      std::cout << " (mandatory option not supplied)";
    }
    std::cout << '\n';
  }

  return EXIT_SUCCESS;
}

Results in:

--group (unknown option)
--id (mandatory option not supplied)
Community
  • 1
  • 1
Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
  • I don't understand `auto get_option_name = [](const std::pair& p) -> const std::string& { return p.first; };` Can you elaborate ? – CMouse Apr 21 '17 at 05:50
  • @CMouse It's a C++11 [lambda](http://en.cppreference.com/w/cpp/language/lambda) that returns a reference to the `first` member of a `const std::pair` that is passed by reference. – Daniel Trebbien Apr 21 '17 at 11:13