2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2010-2018, The GROMACS development team.
5 * Copyright (c) 2019, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
38 * Implements gmx::Options.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_options
50 #include "gromacs/options/abstractoption.h"
51 #include "gromacs/options/abstractoptionstorage.h"
52 #include "gromacs/options/optionsection.h"
53 #include "gromacs/utility/arrayref.h"
54 #include "gromacs/utility/exceptions.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/stringutil.h"
58 #include "options_impl.h"
63 /********************************************************************
67 IOptionManager::~IOptionManager() {}
69 /********************************************************************
73 IOptionsContainer::~IOptionsContainer() {}
75 /********************************************************************
76 * IOptionsContainerWithSections
79 IOptionsContainerWithSections::~IOptionsContainerWithSections() {}
81 /********************************************************************
82 * IOptionSectionStorage
85 IOptionSectionStorage::~IOptionSectionStorage() {}
87 /********************************************************************
94 OptionsImpl::OptionsImpl() : rootSection_(managers_
, nullptr, "") {}
96 /********************************************************************
100 OptionSectionImpl
* OptionSectionImpl::addSectionImpl(const AbstractOptionSection
& section
)
102 const char* name
= section
.name_
;
103 // Make sure that there are no duplicate sections.
104 GMX_RELEASE_ASSERT(findSection(name
) == nullptr, "Duplicate subsection name");
105 std::unique_ptr
<IOptionSectionStorage
> storage(section
.createStorage());
106 subsections_
.push_back(std::make_unique
<OptionSectionImpl
>(managers_
, std::move(storage
), name
));
107 return subsections_
.back().get();
110 IOptionsContainer
& OptionSectionImpl::addGroup()
112 return rootGroup_
.addGroup();
115 OptionInfo
* OptionSectionImpl::addOptionImpl(const AbstractOption
& settings
)
117 return rootGroup_
.addOptionImpl(settings
);
120 OptionSectionImpl
* OptionSectionImpl::findSection(const char* name
) const
122 for (const auto& section
: subsections_
)
124 if (section
->name_
== name
)
126 return section
.get();
132 AbstractOptionStorage
* OptionSectionImpl::findOption(const char* name
) const
134 OptionMap::const_iterator i
= optionMap_
.find(name
);
135 if (i
== optionMap_
.end())
139 return i
->second
.get();
142 void OptionSectionImpl::start()
144 for (const auto& entry
: optionMap_
)
146 entry
.second
->startSource();
148 if (storage_
!= nullptr)
150 if (!storageInitialized_
)
152 storage_
->initStorage();
153 storageInitialized_
= true;
155 storage_
->startSection();
159 void OptionSectionImpl::finish()
161 // TODO: Consider how to customize these error messages based on context.
162 ExceptionInitializer
errors("Invalid input values");
163 for (const auto& entry
: optionMap_
)
165 AbstractOptionStorage
& option
= *entry
.second
;
170 catch (UserInputError
& ex
)
172 ex
.prependContext("In option " + option
.name());
173 errors
.addCurrentExceptionAsNested();
176 if (errors
.hasNestedExceptions())
178 // TODO: This exception type may not always be appropriate.
179 GMX_THROW(InvalidInputError(errors
));
181 if (storage_
!= nullptr)
183 storage_
->finishSection();
187 /********************************************************************
188 * OptionSectionImpl::Group
191 IOptionsContainer
& OptionSectionImpl::Group::addGroup()
193 subgroups_
.emplace_back(parent_
);
194 return subgroups_
.back();
197 OptionInfo
* OptionSectionImpl::Group::addOptionImpl(const AbstractOption
& settings
)
199 OptionSectionImpl::AbstractOptionStoragePointer
option(settings
.createStorage(parent_
->managers_
));
200 options_
.reserve(options_
.size() + 1);
201 auto insertionResult
= parent_
->optionMap_
.insert(std::make_pair(option
->name(), std::move(option
)));
202 if (!insertionResult
.second
)
204 const std::string
& name
= insertionResult
.first
->second
->name();
205 GMX_THROW(APIError("Duplicate option: " + name
));
207 AbstractOptionStorage
& insertedOption
= *insertionResult
.first
->second
;
208 options_
.push_back(&insertedOption
);
209 return &insertedOption
.optionInfo();
212 } // namespace internal
214 using internal::OptionsImpl
;
216 /********************************************************************
220 Options::Options() : impl_(new OptionsImpl
) {}
222 Options::~Options() {}
225 void Options::addManager(IOptionManager
* manager
)
227 // This ensures that all options see the same set of managers.
228 GMX_RELEASE_ASSERT(impl_
->rootSection_
.optionMap_
.empty(),
229 "Can only add a manager before options");
230 // This check could be relaxed if we instead checked that the subsections
231 // do not have options.
232 GMX_RELEASE_ASSERT(impl_
->rootSection_
.subsections_
.empty(),
233 "Can only add a manager before subsections");
234 impl_
->managers_
.add(manager
);
237 internal::OptionSectionImpl
* Options::addSectionImpl(const AbstractOptionSection
& section
)
239 return impl_
->rootSection_
.addSectionImpl(section
);
242 IOptionsContainer
& Options::addGroup()
244 return impl_
->rootSection_
.addGroup();
247 OptionInfo
* Options::addOptionImpl(const AbstractOption
& settings
)
249 return impl_
->rootSection_
.addOptionImpl(settings
);
252 OptionSectionInfo
& Options::rootSection()
254 return impl_
->rootSection_
.info();
257 const OptionSectionInfo
& Options::rootSection() const
259 return impl_
->rootSection_
.info();
262 void Options::finish()
264 impl_
->rootSection_
.finish();