{"action":"created","comment":{"url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/comments/131780104","pull_request_review_id":54788387,"id":131780104,"diff_hunk":"@@ -0,0 +1,181 @@\n+// Distributed under the MIT License.\n+// See LICENSE.txt for details.\n+\n+/// \\file\n+/// Defines class template Factory.\n+\n+#pragma once\n+\n+#include <algorithm>\n+#include <iomanip>\n+#include <memory>\n+#include <sstream>\n+#include <string>\n+#include <type_traits>\n+#include <utility>\n+#include <vector>\n+#include <yaml-cpp/yaml.h>\n+\n+#include \"ErrorHandling/Assert.hpp\"\n+#include \"Options/Options.hpp\"\n+#include \"Utilities/StdHelpers.hpp\"\n+#include \"Utilities/TMPL.hpp\"\n+\n+/// \\ingroup OptionParsing\n+/// \\brief Allows creating a pointer to an abstract base class from an\n+/// input file.\n+///\n+/// To use a factory, the base class (here `BaseClass`) should inherit\n+/// from Factory<BaseClass> and define a static method \\code\n+///  static std::string class_id() { return \"BaseClass\"; }\n+/// \\endcode\n+/// and a type \\code\n+///  using creatable_classes = tmpl::list<Derived1, ...>;\n+/// \\endcode\n+///\n+/// Derived classes should:\n+/// 1) define a static member function std::string class_id() which\n+///    returns a unique id for the derived class, similar to the base\n+///    class method shown above.\n+/// 2) define static OptionString_t help containing class-specfic help\n+///    text\n+/// 3) define a type options as a typelist of option structs required\n+///    to create the class (see Options for details)\n+/// 4) define a constructor taking those options and an OptionContext\n+///\n+/// \\tparam BaseClass the base class of the objects to be created.\n+template <typename BaseClass>\n+class Factory {\n+ public:\n+  using Base_t = BaseClass;\n+\n+  /// Create a derived object.\n+  static std::unique_ptr<BaseClass> create(const Option_t& options);\n+\n+ private:\n+  static std::string help_derived();\n+\n+  template <\n+      typename CreateLs,\n+      typename std::enable_if_t<(tmpl::size<CreateLs>::value != 0)>* = nullptr>\n+  static std::unique_ptr<BaseClass> create_derived(const std::string& id,\n+                                                   const Option_t& opts);\n+\n+  template <\n+      typename CreateLs,\n+      typename std::enable_if_t<(tmpl::size<CreateLs>::value == 0)>* = nullptr>\n+  static std::unique_ptr<BaseClass> create_derived(const std::string& /*id*/,\n+                                                   const Option_t& /*opts*/) {\n+    return std::unique_ptr<BaseClass>{};\n+  }\n+};\n+\n+template <typename BaseClass>\n+template <typename CreateLs,\n+          typename std::enable_if_t<(tmpl::size<CreateLs>::value != 0)>*>\n+std::unique_ptr<BaseClass> Factory<BaseClass>::create_derived(\n+    const std::string& id, const Option_t& opts) {\n+  using derived = tmpl::front<CreateLs>;\n+  // For whatever reason GCC cannot determine whether the create\n+  // functions are the same at compile time.\n+  ASSERT(&derived::class_id != &derived::Base_t::class_id,\n+         \"Registering a class that does not define a class_id function. \"\n+         \"This would result in strange errors when parsing the input file.\");\n+\n+  if (derived::class_id() == id) {\n+    ASSERT(not create_derived<tmpl::pop_front<CreateLs>>(id, opts),\n+           \"Duplicate factory id: \" << id);\n+\n+    Options<typename derived::options> options(derived::help);\n+    options.parse(opts);\n+    return options.template apply<typename derived::options>(\n+        [&opts](auto&&... args) {\n+          return std::make_unique<derived>(std::move(args)..., opts.Context());\n+        });\n+  } else {\n+    return create_derived<tmpl::pop_front<CreateLs>>(id, opts);\n+  }\n+}\n+\n+template <typename BaseClass>\n+std::unique_ptr<BaseClass> Factory<BaseClass>::create(const Option_t& options) {\n+  const auto& node = options.Node();\n+  Option_t derived_opts(options.Context());\n+  std::string id;\n+  if (node.IsScalar()) {\n+    id = node.as<std::string>();\n+  } else if (node.IsMap()) {\n+    if (node.size() != 1) {\n+      PARSE_ERROR(options.Context(),\n+                  \"Expected a single class to create, got \"\n+                  << node.size() << \":\\n\" << node);\n+    }\n+    id = node.begin()->first.as<std::string>();\n+    derived_opts.set_node(node.begin()->second);\n+  } else {\n+    PARSE_ERROR(options.Context(),\n+                \"Expected a class or a class with options, got:\\n\"\n+                << node);\n+  }\n+  derived_opts.Context().append(\"While creating type \" + id);\n+  auto derived =\n+      create_derived<typename BaseClass::creatable_classes>(id, derived_opts);\n+  if (derived) {","path":"src/Options/Factory.hpp","position":123,"original_position":123,"commit_id":"d9a841c5c7e060f3e007d08c78563bedcecdb89b","original_commit_id":"9cd925bdaf454c733510560e2beafc5150314556","user":{"login":"nilsdeppe","id":13531030,"avatar_url":"https://avatars3.githubusercontent.com/u/13531030?v=4","gravatar_id":"","url":"https://api.github.com/users/nilsdeppe","html_url":"https://github.com/nilsdeppe","followers_url":"https://api.github.com/users/nilsdeppe/followers","following_url":"https://api.github.com/users/nilsdeppe/following{/other_user}","gists_url":"https://api.github.com/users/nilsdeppe/gists{/gist_id}","starred_url":"https://api.github.com/users/nilsdeppe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nilsdeppe/subscriptions","organizations_url":"https://api.github.com/users/nilsdeppe/orgs","repos_url":"https://api.github.com/users/nilsdeppe/repos","events_url":"https://api.github.com/users/nilsdeppe/events{/privacy}","received_events_url":"https://api.github.com/users/nilsdeppe/received_events","type":"User","site_admin":false},"body":"`derived != nullptr` for clarity","created_at":"2017-08-07T22:28:39Z","updated_at":"2017-08-07T22:39:32Z","html_url":"https://github.com/sxs-collaboration/spectre/pull/142#discussion_r131780104","pull_request_url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142","_links":{"self":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/comments/131780104"},"html":{"href":"https://github.com/sxs-collaboration/spectre/pull/142#discussion_r131780104"},"pull_request":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142"}}},"pull_request":{"url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142","id":134043816,"html_url":"https://github.com/sxs-collaboration/spectre/pull/142","diff_url":"https://github.com/sxs-collaboration/spectre/pull/142.diff","patch_url":"https://github.com/sxs-collaboration/spectre/pull/142.patch","issue_url":"https://api.github.com/repos/sxs-collaboration/spectre/issues/142","number":142,"state":"open","locked":false,"title":"Option parsing","user":{"login":"wthrowe","id":867117,"avatar_url":"https://avatars1.githubusercontent.com/u/867117?v=4","gravatar_id":"","url":"https://api.github.com/users/wthrowe","html_url":"https://github.com/wthrowe","followers_url":"https://api.github.com/users/wthrowe/followers","following_url":"https://api.github.com/users/wthrowe/following{/other_user}","gists_url":"https://api.github.com/users/wthrowe/gists{/gist_id}","starred_url":"https://api.github.com/users/wthrowe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wthrowe/subscriptions","organizations_url":"https://api.github.com/users/wthrowe/orgs","repos_url":"https://api.github.com/users/wthrowe/repos","events_url":"https://api.github.com/users/wthrowe/events{/privacy}","received_events_url":"https://api.github.com/users/wthrowe/received_events","type":"User","site_admin":false},"body":"I have made many changes from the original code.  Some of the highlights:\r\n* Most of the documentation and error output has been rewritten, including more checks for incorrect input.\r\n* The parser-enforced value checks have been simplified: bounds checks on individual container elements are gone, fixed size vectors are gone (but could still be obtained using min size=max size), and all remaining constraints are shown in the help output.\r\n* Formatting checks are done on option help.  (These existed before but were not run due to a bug.)\r\n* `MAKE_CREATABLE_FROM_YAML` macro for non-factory stuff as discussed elsewhere.\r\n* A \"forwarding\" form for `MAKE_CREATABLE_FROM_YAML` to allow parsing simple types without using full option parsing (e.g., parsing an old-style Point class as `Center: [1, 2, 3]` or an enum as `Norm: L2`).\r\n* Constructors called from Factory and MCFY now take an `OptionContext` parameter that can be passed to the `PARSE_ERROR` macro to include information about the input file location in an error message (generally for doing more complex checks on input values).\r\n* `OptionsList` in factory/MCFY classes has been renamed to `options` because it should be camel case and it isn't a list in MCFY forwarding.\r\n* Added `Options<...>::apply\r\n\r\nI've put some examples of parsing error output at http://astro.cornell.edu/~wthrowe/parsing_errors (executable at http://astro.cornell.edu/~wthrowe/parsing_errors.cpp).","created_at":"2017-08-03T21:18:20Z","updated_at":"2017-08-07T22:39:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f2ba419b973310f73f2a297f29a60707b0e58cbf","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142/commits","review_comments_url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142/comments","review_comment_url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/comments{/number}","comments_url":"https://api.github.com/repos/sxs-collaboration/spectre/issues/142/comments","statuses_url":"https://api.github.com/repos/sxs-collaboration/spectre/statuses/d9a841c5c7e060f3e007d08c78563bedcecdb89b","head":{"label":"wthrowe:options","ref":"options","sha":"d9a841c5c7e060f3e007d08c78563bedcecdb89b","user":{"login":"wthrowe","id":867117,"avatar_url":"https://avatars1.githubusercontent.com/u/867117?v=4","gravatar_id":"","url":"https://api.github.com/users/wthrowe","html_url":"https://github.com/wthrowe","followers_url":"https://api.github.com/users/wthrowe/followers","following_url":"https://api.github.com/users/wthrowe/following{/other_user}","gists_url":"https://api.github.com/users/wthrowe/gists{/gist_id}","starred_url":"https://api.github.com/users/wthrowe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wthrowe/subscriptions","organizations_url":"https://api.github.com/users/wthrowe/orgs","repos_url":"https://api.github.com/users/wthrowe/repos","events_url":"https://api.github.com/users/wthrowe/events{/privacy}","received_events_url":"https://api.github.com/users/wthrowe/received_events","type":"User","site_admin":false},"repo":{"id":96371688,"name":"spectre","full_name":"wthrowe/spectre","owner":{"login":"wthrowe","id":867117,"avatar_url":"https://avatars1.githubusercontent.com/u/867117?v=4","gravatar_id":"","url":"https://api.github.com/users/wthrowe","html_url":"https://github.com/wthrowe","followers_url":"https://api.github.com/users/wthrowe/followers","following_url":"https://api.github.com/users/wthrowe/following{/other_user}","gists_url":"https://api.github.com/users/wthrowe/gists{/gist_id}","starred_url":"https://api.github.com/users/wthrowe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wthrowe/subscriptions","organizations_url":"https://api.github.com/users/wthrowe/orgs","repos_url":"https://api.github.com/users/wthrowe/repos","events_url":"https://api.github.com/users/wthrowe/events{/privacy}","received_events_url":"https://api.github.com/users/wthrowe/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/wthrowe/spectre","description":null,"fork":true,"url":"https://api.github.com/repos/wthrowe/spectre","forks_url":"https://api.github.com/repos/wthrowe/spectre/forks","keys_url":"https://api.github.com/repos/wthrowe/spectre/keys{/key_id}","collaborators_url":"https://api.github.com/repos/wthrowe/spectre/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/wthrowe/spectre/teams","hooks_url":"https://api.github.com/repos/wthrowe/spectre/hooks","issue_events_url":"https://api.github.com/repos/wthrowe/spectre/issues/events{/number}","events_url":"https://api.github.com/repos/wthrowe/spectre/events","assignees_url":"https://api.github.com/repos/wthrowe/spectre/assignees{/user}","branches_url":"https://api.github.com/repos/wthrowe/spectre/branches{/branch}","tags_url":"https://api.github.com/repos/wthrowe/spectre/tags","blobs_url":"https://api.github.com/repos/wthrowe/spectre/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/wthrowe/spectre/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/wthrowe/spectre/git/refs{/sha}","trees_url":"https://api.github.com/repos/wthrowe/spectre/git/trees{/sha}","statuses_url":"https://api.github.com/repos/wthrowe/spectre/statuses/{sha}","languages_url":"https://api.github.com/repos/wthrowe/spectre/languages","stargazers_url":"https://api.github.com/repos/wthrowe/spectre/stargazers","contributors_url":"https://api.github.com/repos/wthrowe/spectre/contributors","subscribers_url":"https://api.github.com/repos/wthrowe/spectre/subscribers","subscription_url":"https://api.github.com/repos/wthrowe/spectre/subscription","commits_url":"https://api.github.com/repos/wthrowe/spectre/commits{/sha}","git_commits_url":"https://api.github.com/repos/wthrowe/spectre/git/commits{/sha}","comments_url":"https://api.github.com/repos/wthrowe/spectre/comments{/number}","issue_comment_url":"https://api.github.com/repos/wthrowe/spectre/issues/comments{/number}","contents_url":"https://api.github.com/repos/wthrowe/spectre/contents/{+path}","compare_url":"https://api.github.com/repos/wthrowe/spectre/compare/{base}...{head}","merges_url":"https://api.github.com/repos/wthrowe/spectre/merges","archive_url":"https://api.github.com/repos/wthrowe/spectre/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/wthrowe/spectre/downloads","issues_url":"https://api.github.com/repos/wthrowe/spectre/issues{/number}","pulls_url":"https://api.github.com/repos/wthrowe/spectre/pulls{/number}","milestones_url":"https://api.github.com/repos/wthrowe/spectre/milestones{/number}","notifications_url":"https://api.github.com/repos/wthrowe/spectre/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/wthrowe/spectre/labels{/name}","releases_url":"https://api.github.com/repos/wthrowe/spectre/releases{/id}","deployments_url":"https://api.github.com/repos/wthrowe/spectre/deployments","created_at":"2017-07-06T00:23:14Z","updated_at":"2017-07-06T00:23:15Z","pushed_at":"2017-08-03T20:48:07Z","git_url":"git://github.com/wthrowe/spectre.git","ssh_url":"git@github.com:wthrowe/spectre.git","clone_url":"https://github.com/wthrowe/spectre.git","svn_url":"https://github.com/wthrowe/spectre","homepage":null,"size":7273,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"sxs-collaboration:develop","ref":"develop","sha":"4a29d6dda869141d1dd26d577a0c273e3e5098d8","user":{"login":"sxs-collaboration","id":26882595,"avatar_url":"https://avatars2.githubusercontent.com/u/26882595?v=4","gravatar_id":"","url":"https://api.github.com/users/sxs-collaboration","html_url":"https://github.com/sxs-collaboration","followers_url":"https://api.github.com/users/sxs-collaboration/followers","following_url":"https://api.github.com/users/sxs-collaboration/following{/other_user}","gists_url":"https://api.github.com/users/sxs-collaboration/gists{/gist_id}","starred_url":"https://api.github.com/users/sxs-collaboration/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sxs-collaboration/subscriptions","organizations_url":"https://api.github.com/users/sxs-collaboration/orgs","repos_url":"https://api.github.com/users/sxs-collaboration/repos","events_url":"https://api.github.com/users/sxs-collaboration/events{/privacy}","received_events_url":"https://api.github.com/users/sxs-collaboration/received_events","type":"Organization","site_admin":false},"repo":{"id":87570510,"name":"spectre","full_name":"sxs-collaboration/spectre","owner":{"login":"sxs-collaboration","id":26882595,"avatar_url":"https://avatars2.githubusercontent.com/u/26882595?v=4","gravatar_id":"","url":"https://api.github.com/users/sxs-collaboration","html_url":"https://github.com/sxs-collaboration","followers_url":"https://api.github.com/users/sxs-collaboration/followers","following_url":"https://api.github.com/users/sxs-collaboration/following{/other_user}","gists_url":"https://api.github.com/users/sxs-collaboration/gists{/gist_id}","starred_url":"https://api.github.com/users/sxs-collaboration/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sxs-collaboration/subscriptions","organizations_url":"https://api.github.com/users/sxs-collaboration/orgs","repos_url":"https://api.github.com/users/sxs-collaboration/repos","events_url":"https://api.github.com/users/sxs-collaboration/events{/privacy}","received_events_url":"https://api.github.com/users/sxs-collaboration/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/sxs-collaboration/spectre","description":null,"fork":false,"url":"https://api.github.com/repos/sxs-collaboration/spectre","forks_url":"https://api.github.com/repos/sxs-collaboration/spectre/forks","keys_url":"https://api.github.com/repos/sxs-collaboration/spectre/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sxs-collaboration/spectre/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sxs-collaboration/spectre/teams","hooks_url":"https://api.github.com/repos/sxs-collaboration/spectre/hooks","issue_events_url":"https://api.github.com/repos/sxs-collaboration/spectre/issues/events{/number}","events_url":"https://api.github.com/repos/sxs-collaboration/spectre/events","assignees_url":"https://api.github.com/repos/sxs-collaboration/spectre/assignees{/user}","branches_url":"https://api.github.com/repos/sxs-collaboration/spectre/branches{/branch}","tags_url":"https://api.github.com/repos/sxs-collaboration/spectre/tags","blobs_url":"https://api.github.com/repos/sxs-collaboration/spectre/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sxs-collaboration/spectre/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sxs-collaboration/spectre/git/refs{/sha}","trees_url":"https://api.github.com/repos/sxs-collaboration/spectre/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sxs-collaboration/spectre/statuses/{sha}","languages_url":"https://api.github.com/repos/sxs-collaboration/spectre/languages","stargazers_url":"https://api.github.com/repos/sxs-collaboration/spectre/stargazers","contributors_url":"https://api.github.com/repos/sxs-collaboration/spectre/contributors","subscribers_url":"https://api.github.com/repos/sxs-collaboration/spectre/subscribers","subscription_url":"https://api.github.com/repos/sxs-collaboration/spectre/subscription","commits_url":"https://api.github.com/repos/sxs-collaboration/spectre/commits{/sha}","git_commits_url":"https://api.github.com/repos/sxs-collaboration/spectre/git/commits{/sha}","comments_url":"https://api.github.com/repos/sxs-collaboration/spectre/comments{/number}","issue_comment_url":"https://api.github.com/repos/sxs-collaboration/spectre/issues/comments{/number}","contents_url":"https://api.github.com/repos/sxs-collaboration/spectre/contents/{+path}","compare_url":"https://api.github.com/repos/sxs-collaboration/spectre/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sxs-collaboration/spectre/merges","archive_url":"https://api.github.com/repos/sxs-collaboration/spectre/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sxs-collaboration/spectre/downloads","issues_url":"https://api.github.com/repos/sxs-collaboration/spectre/issues{/number}","pulls_url":"https://api.github.com/repos/sxs-collaboration/spectre/pulls{/number}","milestones_url":"https://api.github.com/repos/sxs-collaboration/spectre/milestones{/number}","notifications_url":"https://api.github.com/repos/sxs-collaboration/spectre/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sxs-collaboration/spectre/labels{/name}","releases_url":"https://api.github.com/repos/sxs-collaboration/spectre/releases{/id}","deployments_url":"https://api.github.com/repos/sxs-collaboration/spectre/deployments","created_at":"2017-04-07T17:28:20Z","updated_at":"2017-07-30T21:43:07Z","pushed_at":"2017-08-07T22:15:35Z","git_url":"git://github.com/sxs-collaboration/spectre.git","ssh_url":"git@github.com:sxs-collaboration/spectre.git","clone_url":"https://github.com/sxs-collaboration/spectre.git","svn_url":"https://github.com/sxs-collaboration/spectre","homepage":null,"size":19421,"stargazers_count":4,"watchers_count":4,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":6,"mirror_url":null,"open_issues_count":26,"forks":6,"open_issues":26,"watchers":4,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142"},"html":{"href":"https://github.com/sxs-collaboration/spectre/pull/142"},"issue":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/issues/142"},"comments":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/issues/142/comments"},"review_comments":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142/comments"},"review_comment":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/pulls/142/commits"},"statuses":{"href":"https://api.github.com/repos/sxs-collaboration/spectre/statuses/d9a841c5c7e060f3e007d08c78563bedcecdb89b"}}}}
{"actor":{"display_login":"nilsdeppe"}}