Browse Source

docs: update macros and README

Sam Jaffe 2 years ago
parent
commit
d0c04ad449
2 changed files with 60 additions and 23 deletions
  1. 50 18
      README.md
  2. 10 5
      include/program_args/arguments.h

+ 50 - 18
README.md

@@ -18,24 +18,45 @@ In either case, options with an arity greater than one are not allowed.
 Abbreviated options support key-value concatenation, such as how you can do `-I/path/to/my/include/dir` in gcc/clang.
 
 #### Snippets
+Let's take a look at a partial example of how we might design the interfact for git using this tool.
 
-A partial git command arguments:
+For conveniece sake, we'll start with a helper object.
 ```c++
-struct Push : program::Arguments<Push> {
+struct GitBranch {
+  GitBranch(Git const *context); // Must be implemented later
+
+  std::string name() const;
+  std::string remote() const;
+  // ...more inspectors
+};
+
+template <typename CRTP> struct GitAction : program::Arguments<CRTP> {
+protected:
   using Arguments::Arguments;
+  GitBranch branch() const { return GitBranch(parent<Git>()); }
+};
+```
+
+Now, we start do define some actions. For the sake of this example, we'll take a look only at push and pull.
+The commands git-push and git-pull have the interesting characteristic that if the current branch is a remote-tracking branch, then we can automatically pick out the the remote and push/pull it without specifying which remote OR which branch is selected.
+```c++
+struct Push : GitAction<Push> {
+  using GitAction::GitAction;
+
   bool atomic = flag("atomic");
   bool dry_run = flag("dry-run", 'n') = false;
   bool no_verify = flag("no-verify", "skip push hooks") = false;
   bool quiet = flag("quiet", 'q') = false;
   bool verbose = flag("verbose", 'v') = false;
   // ...more options
-  // TODO: Fetch Upstream Implicitly
-  std::string repository = argument(0, "repository") = "";
-  std::vector<std::string> refspec = rest("refspec");
+
+  std::string repository = argument(0, "repository") = PROGRAM_DEFER(branch().remote());
+  std::vector<std::string> refspec = rest("refspec") = PROGRAM_DEFER(branch().name());
 };
 
-struct Pull : program::Arguments<Pull> {
-  using Arguments::Arguments;
+struct Pull : GitAction<Pull> {
+  using GitAction::GitAction;
+
   bool quiet = flag("quiet", 'q') = false;
   bool verbose = flag("verbose", 'v') = false;
   bool commit = flag("commit", "immediately commit the merged result") = true;
@@ -43,30 +64,41 @@ struct Pull : program::Arguments<Pull> {
   bool fastforward = flag("ff", "resolve the merge with a fast-forward if possible") = true;
   bool only_ff = flag("ff-only", "fail if fast-forwarding will not succeed");
   // ...more options
-  // TODO: Fetch Upstream Implicitly
-  std::string repository = argument(0, "repository") = "";
-  std::vector<std::string> refspec = rest("refspec");
-};
 
-struct Commit : program::Arguments<Commit> {
-  using Arguments::Arguments;
+  std::string repository = argument(0, "repository") = PROGRAM_DEFER(branch().remote());
+  std::vector<std::string> refspec = rest("refspec") = PROGRAM_DEFER(branch().name());
 };
+```
 
+And finally we can define the actual Git command. The macro PROGRAM\_ARGS\_INVOKE creates a method invoke(), which iterates through all of the actions, and checks to see if they were set. If an action is set, then we will automagically recurse into its own invoke function, creating a chain of actions that will be passed into a TYPED\_MAIN function.
+```c++
 struct Git : program::Arguments<Git> {
 public:
-  using Arguments::Arguments;
+  // Automatically fails if no action was input
+  PROGRAM_ARGS_INVOKE(commit, push, pull);
+
   std::string pwd = option('C');
   std::string git_dir = option("git-dir") = PROGRAM_DEFER(locate_git_dir(pwd));
   std::map<std::string, std::string> config_env = option("config-env");
   // ...more options
-private:
-  friend int main(int, char const * const*);
-  Commit commit = action("commit");
+
+private: // PROGRAM_ARGS_INVOKE is the only one who needs to access these
   Push push = action("push");
   Pull pull = action("pull");
 };
+```
+
+For each series of "actions" that we support - we instantiate a typed\_main function. A combination of the PROGRAM\_ARGS\_MAIN macro and the PROGRAM\_ARGS\_INVOKE macro will automatically dispatch the correct action selected to the correct TYPED\_MAIN body.
+```c++
+TYPED_MAIN(Git const &git, Push const &push) {
+  ...
+}
+
+TYPED_MAIN(Git const &git, Pull const &pull) {
+  ...
+}
 
-TYPED_MAIN(Git)
+PROGRAM_ARGS_MAIN(Git)
 ```
 
 Singular option storage cannot be repeated

+ 10 - 5
include/program_args/arguments.h

@@ -271,13 +271,20 @@ struct Arguments<Impl>::WithDefault {
 template <typename Args, typename... Actions>
 int typed_main(Args const &, Actions const &...);
 
-#define PROGRAM_ARGS_INVOKE_WITH_DEFAULT(default, ...)                         \
+#define PROGRAM_ARGS_INVOKE_WITH_DEFAULT_P(super, default, ...)                \
   template <typename... Parents>                                               \
   int invoke(Parents const &... parents) const {                               \
     return invoke_action(default, program::actions(__VA_ARGS__),               \
                          program::count(__VA_ARGS__), parents...);             \
   }                                                                            \
-  using Arguments::Arguments
+  using super::super
+
+#define PROGRAM_ARGS_INVOKE_WITH_DEFAULT(default, ...)                         \
+  PROGRAM_ARGS_INVOKE_WITH_DEFAULT_P(Arguments, default, __VA_ARGS__)
+
+#define PROGRAM_ARGS_INVOKE_P(super, ...)                                      \
+  PROGRAM_ARGS_INVOKE_WITH_DEFAULT_P(super, program::require_action,           \
+                                     __VA_ARGS__)
 
 #define PROGRAM_ARGS_INVOKE(...)                                               \
   PROGRAM_ARGS_INVOKE_WITH_DEFAULT(program::require_action, __VA_ARGS__)
@@ -289,8 +296,6 @@ int typed_main(Args const &, Actions const &...);
     std::cerr << "Error in program argument handling: " << pae.what() << "\n"; \
   }
 
-#define TYPED_MAIN(tname)                                                      \
-  PROGRAM_ARGS_MAIN(tname)                                                     \
-  template <> int typed_main(tname const & args)
+#define TYPED_MAIN(...) template <> int typed_main(__VA_ARGS__)
 
 #define PROGRAM_DEFER(...) [this]() { return __VA_ARGS__; }