Allowing additional arguments to be passed to split_step's functor
authorMarcelo Juchem <marcelo@fb.com>
Tue, 3 Jun 2014 21:40:29 +0000 (14:40 -0700)
committerAnton Likhtarov <alikhtarov@fb.com>
Mon, 9 Jun 2014 22:35:55 +0000 (15:35 -0700)
Summary: more flexibility for using functors with split_step

Test Plan: unit tests added + arc unit

Reviewed By: ldbrandy@fb.com

Subscribers: folly@lists

FB internal diff: D1362644

folly/Range.h
folly/test/RangeTest.cpp

index 0043c703233eee1fa8e20a5d1b1473ca9d23967c..3d9e8d51f9da5063f89e11cefbe83da8378f01b6 100644 (file)
@@ -611,7 +611,8 @@ public:
 
   /**
    * Convenience method that calls `split_step()` and passes the result to a
-   * functor, returning whatever the functor does.
+   * functor, returning whatever the functor does. Any additional arguments
+   * `args` passed to this function are perfectly forwarded to the functor.
    *
    * Say you have a functor with this signature:
    *
@@ -636,17 +637,49 @@ public:
    *    // ...
    *  }
    *
+   *  struct Foo {
+   *    void parse(folly::StringPiece s) {
+   *      s.split_step(' ', parse_field, bar, 10);
+   *      s.split_step('\t', parse_field, baz, 20);
+   *
+   *      auto const kludge = [](folly::StringPiece x, int &out, int def) {
+   *        if (x == "null") {
+   *          out = 0;
+   *        } else {
+   *          parse_field(x, out, def);
+   *        }
+   *      };
+   *
+   *      s.split_step('\t', kludge, gaz);
+   *      s.split_step(' ', kludge, foo);
+   *    }
+   *
+   *  private:
+   *    int bar;
+   *    int baz;
+   *    int gaz;
+   *    int foo;
+   *
+   *    static parse_field(folly::StringPiece s, int &out, int def) {
+   *      try {
+   *        out = folly::to<int>(s);
+   *      } catch (std::exception const &) {
+   *        value = def;
+   *      }
+   *    }
+   *  };
+   *
    * @author: Marcelo Juchem <marcelo@fb.com>
    */
-  template <typename TProcess>
-  auto split_step(value_type delimiter, TProcess &&process)
-    -> decltype(process(std::declval<Range>()))
-  { return process(split_step(delimiter)); }
-
-  template <typename TProcess>
-  auto split_step(Range delimiter, TProcess &&process)
-    -> decltype(process(std::declval<Range>()))
-  { return process(split_step(delimiter)); }
+  template <typename TProcess, typename... Args>
+  auto split_step(value_type delimiter, TProcess &&process, Args &&...args)
+    -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...))
+  { return process(split_step(delimiter), std::forward<Args>(args)...); }
+
+  template <typename TProcess, typename... Args>
+  auto split_step(Range delimiter, TProcess &&process, Args &&...args)
+    -> decltype(process(std::declval<Range>(), std::forward<Args>(args)...))
+  { return process(split_step(delimiter), std::forward<Args>(args)...); }
 
 private:
   Iter b_, e_;
index 681aa03d410dfbc7077626e0b9909c8f292835cc..8cbbfb0924f0819bf2dff68de89b4deeef24cf22 100644 (file)
@@ -439,6 +439,7 @@ TEST(StringPiece, split_step_char_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   auto x = p.split_step(' ');
   EXPECT_EQ(std::next(s, 5), p.begin());
@@ -496,6 +497,7 @@ TEST(StringPiece, split_step_range_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   auto x = p.split_step("  ");
   EXPECT_EQ(std::next(s, 6), p.begin());
@@ -560,6 +562,7 @@ TEST(StringPiece, split_step_with_process_char_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   EXPECT_EQ(1, (p.split_step(' ', [&](folly::StringPiece x) {
     EXPECT_EQ(std::next(s, 5), p.begin());
@@ -642,6 +645,7 @@ TEST(StringPiece, split_step_with_process_range_delimiter) {
   folly::StringPiece p(s);
   EXPECT_EQ(s, p.begin());
   EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
 
   EXPECT_EQ(1, (p.split_step("  ", [&](folly::StringPiece x) {
     EXPECT_EQ(std::next(s, 6), p.begin());
@@ -721,6 +725,82 @@ TEST(StringPiece, split_step_with_process_range_delimiter) {
   EXPECT_NO_THROW(p.split_step(' ', split_step_with_process_noop));
 }
 
+TEST(StringPiece, split_step_with_process_char_delimiter_additional_args) {
+  //              0         1         2
+  //              012345678901234567890123456
+  auto const s = "this is just  a test string";
+  auto const e = std::next(s, std::strlen(s));
+  auto const delimiter = ' ';
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto const functor = [](
+    folly::StringPiece s,
+    folly::StringPiece expected
+  ) {
+    EXPECT_EQ(expected, s);
+    return expected;
+  };
+
+  auto const checker = [&](folly::StringPiece expected) {
+    EXPECT_EQ(expected, p.split_step(delimiter, functor, expected));
+  };
+
+  checker("this");
+  checker("is");
+  checker("just");
+  checker("");
+  checker("a");
+  checker("test");
+  checker("string");
+  checker("");
+  checker("");
+
+  EXPECT_TRUE(p.empty());
+}
+
+TEST(StringPiece, split_step_with_process_range_delimiter_additional_args) {
+  //              0         1         2         3
+  //              0123456789012345678901234567890123
+  auto const s = "this  is  just    a   test  string";
+  auto const e = std::next(s, std::strlen(s));
+  auto const delimiter = "  ";
+  EXPECT_EQ('\0', *e);
+
+  folly::StringPiece p(s);
+  EXPECT_EQ(s, p.begin());
+  EXPECT_EQ(e, p.end());
+  EXPECT_EQ(s, p);
+
+  auto const functor = [](
+    folly::StringPiece s,
+    folly::StringPiece expected
+  ) {
+    EXPECT_EQ(expected, s);
+    return expected;
+  };
+
+  auto const checker = [&](folly::StringPiece expected) {
+    EXPECT_EQ(expected, p.split_step(delimiter, functor, expected));
+  };
+
+  checker("this");
+  checker("is");
+  checker("just");
+  checker("");
+  checker("a");
+  checker(" test");
+  checker("string");
+  checker("");
+  checker("");
+
+  EXPECT_TRUE(p.empty());
+}
+
 TEST(qfind, UInt32_Ranges) {
   vector<uint32_t> a({1, 2, 3, 260, 5});
   vector<uint32_t> b({2, 3, 4});