diff --git a/src/common/component_libmultiscale.hh b/src/common/component_libmultiscale.hh index 35726f4..59cd8b3 100644 --- a/src/common/component_libmultiscale.hh +++ b/src/common/component_libmultiscale.hh @@ -1,1084 +1,963 @@ /** * @file component_libmultiscale.hh * * @author Guillaume Anciaux * * @date Mon Sep 08 23:40:22 2014 * * @brief This describe the root objects to be combined into valid LM * components * * @section LICENSE * * Copyright (©) 2010-2011 EPFL (Ecole Polytechnique Fédérale de Lausanne) * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * LibMultiScale is free software: you can redistribute it and/or modify it * under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * LibMultiScale is distributed in the hope that it will be useful, but * WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with LibMultiScale. If not, see . * */ #ifndef __LIBMULTISCALE_COMPONENT_LIBMULTISCALE_HH__ #define __LIBMULTISCALE_COMPONENT_LIBMULTISCALE_HH__ /* -------------------------------------------------------------------------- */ #include "container.hh" #include "lm_common.hh" #include "lm_object.hh" #include "static_dispatch.hh" /* -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include /* -------------------------------------------------------------------------- */ __BEGIN_LIBMULTISCALE__ /* -------------------------------------------------------------------------- */ namespace detail { template struct foreach_tuple_impl { template static void doIt(F &&f, Tuple &&t) { f(std::get(std::forward(t))); foreach_tuple_impl::doIt(f, t); } }; template <> struct foreach_tuple_impl<-1> { template static void doIt(F &&, Tuple &&) {} }; } // namespace detail template constexpr decltype(auto) foreach_tuple(F &&f, Tuple &&t) { detail::foreach_tuple_impl>>::value - 1>::doIt(std::forward(f), std::forward(t)); } /* -------------------------------------------------------------------------- */ class ContainerInterface; template class ContainerArray; template struct getFirstArgument { // this is a non used case.... // static int get(Tuple &) { return 10; }; }; template struct getFirstArgument> { static auto &get(std::tuple &tuple) { return std::get<0>(tuple); } }; /* -------------------------------------------------------------------------- */ class ReleasedObject { public: ReleasedObject() { release = 0; } //! return actual release UInt getRelease() const { return release; }; //! return actual release void setRelease(UInt r) { release = r; }; //! increment the release void incRelease() { ++release; }; template void acquireRelease(const Dependencies &deps) { for (auto &pair : deps) { auto *ptr = pair.second; if (ptr == nullptr) LM_FATAL(pair.first << ": input was not created/computed/connected"); release = std::max(release, (*ptr)->getRelease()); } } template bool checkDependency(const Dependencies &deps) { for (auto &pair : deps) { if (pair.second == nullptr) return true; try { if (!pair.second->isAllocated()) return true; auto &dep = pair.second->get(); if (release < dep.getRelease()) return true; } catch (...) { return true; } } return false; } private: UInt release; }; struct ArgumentInterface : public ReleasedObject { ArgumentInterface(){}; virtual ~ArgumentInterface(){}; virtual std::type_index get_type_index() const = 0; virtual std::string get_type_info() const = 0; template T &cast(); virtual void printself(std::ostream &os) const = 0; virtual void _copyContainerInfo(ContainerInterface &c) = 0; virtual void setCommGroup(CommGroup &c) = 0; }; inline std::ostream &operator<<(std::ostream &os, const ArgumentInterface &a) { a.printself(os); return os; } template std::enable_if_t>::value> _copyContainerInfo_casted(T1 &value, ContainerInterface &a) { value.copyContainerInfo(a); } template std::enable_if_t>::value> _copyContainerInfo_casted(T1 &, ContainerInterface &) {} template void _copyContainerInfo_casted(std::shared_ptr value, ContainerInterface &a) { T1 &v = *value; _copyContainerInfo_casted(v, a); } template std::enable_if_t>::value> _setCommGroup(T1 &value, CommGroup &c) { value.setCommGroup(c); } template std::enable_if_t>::value> _setCommGroup(T1 &, CommGroup &) {} template void _setCommGroup(std::shared_ptr value, CommGroup &comm_group) { T1 &v = *value; _setCommGroup(v, comm_group); } template struct Argument : public ArgumentInterface { void _copyContainerInfo(ContainerInterface &c) override { _copyContainerInfo_casted(value, c); }; virtual void setCommGroup(CommGroup &c) override { _setCommGroup(value, c); }; Argument(Argument &&arg) = default; Argument(T &&val) : value(std::forward(val)) {} inline void printself(std::ostream &os) const override { const T &temp = this->value; os << "(" << this << ")" << this->get_type_info() << " : " << " " << &temp; } const std::type_index type_index = typeid(T); inline std::type_index get_type_index() const override { return type_index; }; inline std::string get_type_info() const override { return typeinfo(); }; inline virtual T &get() { return value; }; T value; }; template T &ArgumentInterface::cast() { auto ptr1 = dynamic_cast *>(this); auto ptr2 = dynamic_cast *>(this); auto ptr3 = dynamic_cast> *>(this); if (ptr1 == nullptr && ptr2 == nullptr && ptr3 == nullptr) { LM_FATAL("cannot cast argument from " << this->get_type_info() << " into " << typeinfo()); } if (ptr1) return ptr1->get(); if (ptr2) return ptr2->get(); return *ptr3->get(); } class Component; class ArgumentContainer { public: ArgumentContainer() : component(nullptr), ptr(nullptr){}; ArgumentContainer(Component &component) : component(&component), ptr(nullptr){}; ArgumentContainer(ArgumentContainer &&other) = default; ArgumentInterface *operator->() { return &this->get(); } ArgumentInterface &operator*() { return this->get(); } template ArgumentContainer &operator=(Argument &&val) { if (ptr_allocated.get() != nullptr) ptr_allocated.release(); ptr_allocated = std::make_unique>(std::forward(val)); ptr = ptr_allocated.get(); return *this; } UInt getRelease() { return ptr->getRelease(); } void setRelease(UInt release) { ptr->setRelease(release); } ArgumentContainer &operator=(ArgumentContainer &&arg) = default; ArgumentContainer &operator=(const ArgumentContainer &arg) { if (ptr_allocated.get() != nullptr) ptr_allocated.release(); ptr = arg.ptr; return *this; } inline ArgumentInterface &get(bool eval = true); inline const ArgumentInterface &get() const; inline bool isAllocated(); private: Component *component; ArgumentInterface *ptr; std::unique_ptr ptr_allocated; public: class UnallocatedPointer : public std::exception {}; }; template Argument make_argument(T &&val) { return Argument(std::forward(val)); } /* --------------------------------------------------------------------- */ class Component : public ReleasedObject, public virtual LMObject { public: Component() : me_as_argument(*this), calculated_once(false), comm_group(nullptr) { me_as_argument = make_argument(*this); } virtual ~Component(){}; //! set the communication group virtual void setCommGroup(CommGroup &group) { comm_group = &group; }; template void connect(const std::string &input, Argument &&arg); inline void connect(const std::string &input, ArgumentContainer &arg); template inline void setInput(Args &&... args) { auto tup = std::forward_as_tuple(args...); int cpt = 0; std::vector keys; std::for_each(inputs.begin(), inputs.end(), [&keys](auto key) { keys.push_back(key.first); }); auto f = [&](auto &arg) { auto key = keys[cpt]; auto search = inputs.find(key); if (search == inputs.end()) { LM_FATAL(this->getID() + ":" + key + ": input not existing"); } if (this->inputs[key] == nullptr) this->inputs[key] = new ArgumentContainer(); *this->inputs[key] = make_argument(arg); ++cpt; }; apply(f, tup); } inline ArgumentContainer *allocInput(const std::string &input); inline void createOutput(const std::string &output); inline void createInput(const std::string &input); inline void removeInput(const std::string &input); inline bool isConnected(const std::string &input); virtual void compute(); template void buildManual(Args &&... args) { this->setInput(args...); this->compute_make_call(); calculated_once = true; } template std::enable_if_t::value> _copyContainerInfo(T &a) { for (auto &output : this->outputs) { auto &o = output.second.get(false); o._copyContainerInfo(a); } } template std::enable_if_t::value> _copyContainerInfo(T &) {} virtual void compute_make_call() = 0; inline ArgumentContainer &getInput(const std::string &requested_input = "") { std::string input = requested_input; if (input == "") { if (this->inputs.size() == 0) { LM_FATAL("There is no input for " << this->getID()); } else if (this->inputs.size() != 1) { std::string mesg = this->getID() + " has several inputs: need a name"; mesg += "\n\nPossible names: \n\n"; for (auto &&pair : this->inputs) { mesg += "\t" + pair.first + "\n"; } LM_FATAL(mesg); } input = this->inputs.begin()->first; } else { auto search = inputs.find(input); if (search == inputs.end()) { LM_FATAL(this->getID() + ":" + input + ": input not existing"); } } ArgumentContainer *arg_cont_input = inputs[input]; if (arg_cont_input == nullptr) throw UnconnectedInput{"For component '" + this->getID() + "' input '" + input + "' is not connected"}; return *arg_cont_input; } inline auto &getOutputs() { return outputs; } inline auto &getInputs() { return inputs; } inline ArgumentContainer &getOutput(const std::string &output_name = "") { auto name = output_name; if (name == "") { if (this->outputs.size() == 0) { LM_FATAL("There is no output for " << this->getID()); } else if (this->outputs.size() != 1) { std::string mesg = this->getID() + " has several outputs: need a name"; mesg += "\n\nPossible names: \n\n"; for (auto &&pair : this->outputs) { mesg += "\t" + pair.first + "\n"; } LM_FATAL(mesg); } name = this->outputs.begin()->first; } auto search = outputs.find(name); if (search == outputs.end()) { std::string mesg = this->getID() + ":" + name + ": output not existing"; mesg += "\n\nPossible names: \n\n"; for (auto &&pair : this->outputs) { mesg += "\t" + pair.first + "\n"; } LM_FATAL(mesg); } return outputs[name]; }; template auto &getCastedOutput(const std::string &output_name, bool eval = true) { auto &arg = this->getOutput(output_name).get(eval); return arg.cast(); } template auto &allocAndGetCastedOutput(const std::string &output_name, T &&... construction_parameters) { using type = typename Cont::ContainerSubset; using ptr_type = std::shared_ptr; try { this->getCastedOutput(output_name, false); } catch (ArgumentContainer::UnallocatedPointer &e) { this->getOutput(output_name) = make_argument(std::make_shared(construction_parameters...)); } auto &output = *getCastedOutput(output_name, false); return output; } inline void printself(std::ostream &os) const; void clear_inputs() { inputs.clear(); } struct UnconnectedInput : public std::runtime_error { using std::runtime_error::runtime_error; }; struct NotInCommGroup : public std::runtime_error { using std::runtime_error::runtime_error; }; ArgumentContainer &toArgContainer() { return me_as_argument; } template ContainerArray &getArray(const std::string &output_name = "") { return this->getCastedOutput>(output_name, false); } template void createArrayOutputs(Names &&output_names) { for (auto &&f : output_names) { this->createArrayOutput(f); } } template void createArrayOutput(const std::string &name) { this->createOutput(name); this->getOutput(name) = make_argument(ContainerArray(this->getID() + ":" + name)); } private: ArgumentContainer me_as_argument; friend class ActionInterface; std::map inputs; std::map outputs; std::map> allocated_inputs; bool calculated_once; CommGroup *comm_group; }; inline std::ostream &operator<<(std::ostream &os, const Component &comp) { comp.printself(os); return os; } void Component::printself(std::ostream &os) const { for (auto &pair : this->inputs) { os << "input " << pair.first << ":"; if (pair.second != nullptr) os << pair.second->get() << "\n"; else os << "not yet defined/computed\n"; } for (auto &pair : this->outputs) { os << "output " << pair.first << ":"; try { os << pair.second.get() << "\n"; } catch (...) { os << "not yet defined/computed\n"; } } } void Component::connect(const std::string &input, ArgumentContainer &arg) { auto search = inputs.find(input); if (search == inputs.end()) { LM_FATAL(this->getID() + ":" + typeinfo() + ":" + input + ": input not existing"); } inputs[input] = &arg; } template void Component::connect(const std::string &input, Argument &&arg) { auto search = inputs.find(input); if (search == inputs.end()) { LM_FATAL(this->getID() + ":" + typeinfo() + ":" + input + ": input not existing"); } if (inputs[input] == nullptr) { inputs[input] = allocInput(input); } *inputs[input] = std::move(arg); } ArgumentContainer *Component::allocInput(const std::string &input) { allocated_inputs[input] = std::make_unique(); return allocated_inputs[input].get(); } void Component::createOutput(const std::string &output) { outputs[output] = ArgumentContainer(*this); } void Component::createInput(const std::string &input) { inputs[input] = nullptr; } void Component::removeInput(const std::string &input) { inputs.erase(input); } bool Component::isConnected(const std::string &input) { return inputs.count(input) > 0 && inputs[input] != nullptr; } /* --------------------------------------------------------------------- */ bool ArgumentContainer::isAllocated() { return this->ptr != nullptr; } /* --------------------------------------------------------------------- */ ArgumentInterface &ArgumentContainer::get(bool eval) { if (this->component != nullptr && eval) { this->component->compute(); } if (ptr == nullptr) throw UnallocatedPointer{}; return *ptr; } /* --------------------------------------------------------------------- */ const ArgumentInterface &ArgumentContainer::get() const { if (ptr == nullptr) LM_FATAL("unallocated pointer"); return *ptr; } /* --------------------------------------------------------------------- */ + template inline std::enable_if_t::value, type *> cast_to_obj(ArgumentInterface &arg_gotten) { if (auto *ref = dynamic_cast *>(&arg_gotten)) { return &(ref->get()); } return nullptr; } template inline std::enable_if_t::value, type *> cast_to_obj(ArgumentInterface &) { return nullptr; } -// template struct _cpp_dynamic_cast { - -// template -// static void cast(Functor &func, Arg &arg) { - -// using _type = std::remove_pointer_t< -// std::decay_t>>; -// // std::cout << "testing type " << demangle(typeid(_type).name()) << -// // std::endl; -// ArgumentInterface &arg_gotten = arg->get(); -// // std::cout << "with object " << arg_gotten << std::endl; -// if (cast_to_obj<_type>(arg_gotten, func)) { -// return; -// } -// if (cast_to_obj<_type &>(arg_gotten, func)) { -// return; -// } -// if (auto *ref = -// dynamic_cast> *>(&arg_gotten)) { -// func(*(ref->get())); -// return; -// } -// _cpp_dynamic_cast::cast(func, arg); -// } -// }; +/* --------------------------------------------------------------------- */ struct ImpossibleCast : public std::runtime_error { ImpossibleCast(ArgumentContainer &arg) : std::runtime_error("cannot cast component with type : " + arg.get().get_type_info()), arg(arg) {} // std::cerr << typeid(Arg).name() << " " << arg << std::endl; - ArgumentContainer &arg; }; -// template -// struct _cpp_dynamic_cast { - -// template -// static void cast(Functor &, Arg &arg) { -// throw ImpossibleCast(*arg); -// } -// }; - -// template -// void cpp_dynamic_cast(Functor &func, Arg &arg) { -// constexpr size_t N = std::tuple_size::value; -// _cpp_dynamic_cast::cast(func, arg); -// } - -/* --------------------------------------------------------------------- */ - -// template