415: Add meta switch-case; disambiguate typedef_.

https://svn.lrde.epita.fr/svn/oln/trunk/metalic Index: ChangeLog from Thierry Geraud <theo@lrde.epita.fr> Add meta switch-case; disambiguate typedef_. * tests/case.cc: New file. * tests/switch.cc (where_): Precise name. * tests/ret.cc: New file. * mlc/bool.hh (assert): Remove is_true to avoid cryptic error message such as "is_true" inaccessible. * mlc/flags.hh (found): New class. * mlc/typedef.hh (result2): New inner class to disambiguate between "typedef is found but its value is not_found" and "typedef is not found". (mlc_ret, mlc_decl_typedef(ret)): Move to... * mlc/ret.hh: ...this new file. * mlc/switch.hh: Change that makes this file soon obsolete. * mlc/case.hh: New file. mlc/bool.hh | 6 - mlc/case.hh | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mlc/flags.hh | 9 + mlc/ret.hh | 63 +++++++++++++ mlc/switch.hh | 66 ++++++------- mlc/typedef.hh | 40 +++++--- tests/case.cc | 65 +++++++++++++ tests/ret.cc | 17 +++ tests/switch.cc | 36 ++++--- 9 files changed, 503 insertions(+), 66 deletions(-) Index: tests/case.cc --- tests/case.cc (revision 0) +++ tests/case.cc (revision 0) @@ -0,0 +1,65 @@ +#include <mlc/is_a.hh> +#include <mlc/case.hh> + + +struct test; + + +mlc_case_equipment_for_namespace(client); + + +namespace client +{ + + template <class T> + struct case_ <test, T, 1> : public mlc::where_< mlc_is_a(T, short) > + { + }; + + template <class T> + struct case_ <test, T, 2> : public mlc::where_< mlc_is_a(T, int) > + { + }; + + + // ok + + template <class T> + struct case_ <test, T, 3> : public mlc::where_< mlc_is_a(T, char) > + { + }; + + +// // ko + +// template <class T> +// struct case_ <test, T, 0> : public mlc::where_< mlc_is_a(T, char) > +// { +// }; + +// template <class T> +// struct case_ <test, T, 3> +// { +// }; + +// template <class T> +// struct default_case_ <test, T> : public mlc::where_< mlc::true_ > +// { +// }; + + +} // end of namespace client + + +template <class T> +void print() +{ + int tmp; +} + + + +int main() +{ + print< client::case_<test, char>::ret >(); +} Index: tests/switch.cc --- tests/switch.cc (revision 414) +++ tests/switch.cc (working copy) @@ -1,45 +1,55 @@ #include <mlc/is_a.hh> -#include <mlc/switch.hh> +#include <mlc/case.hh> struct test; -namespace mlc +mlc_case_equipment_for_namespace(client); + + +namespace client { template <class T> - struct case_ <test, T, 1> : public where_< mlc_is_a(T, short) > + struct case_ <test, T, 1> : public mlc::where_< mlc_is_a(T, short) > { typedef float ret; }; template <class T> - struct case_ <test, T, 2> : public where_< mlc_is_a(T, int) > + struct case_ <test, T, 2> : public mlc::where_< mlc_is_a(T, int) > { typedef double ret; }; - // // ok + template <class T> + struct case_ <test, T, 3> : public mlc::where_< mlc_is_a(T, char) > + { + typedef int ret; + }; + +// // or: + // template <class T> -// struct case_ <test, T, 3> : public where_< mlc_is_a(T, char) > +// struct case_ <test, T, 3> : public mlc::where_< mlc_is_a(T, char) > // { -// typedef int ret; +// typedef mlc::not_found ret; // }; // // ko // template <class T> -// struct case_ <test, T, 3> : public where_< mlc_is_a(T, char) > +// struct case_ <test, T, 0> : public mlc::where_< mlc_is_a(T, char) > // { -// typedef not_found ret; +// typedef int ret; // }; // template <class T> -// struct case_ <test, T, 3> : public where_< mlc_is_a(T, char) > +// struct case_ <test, T, 3> : public mlc::where_< mlc_is_a(T, char) > // { // }; @@ -50,7 +60,7 @@ // }; // template <class T> -// struct default_case_ <test, T> : public where_< mlc::true_ > +// struct default_case_ <test, T> : public mlc::where_< mlc::true_ > // { // typedef int ret; // }; @@ -61,7 +71,7 @@ // }; -} // end of namespace mlc +} // end of namespace client template <class T> @@ -74,5 +84,5 @@ int main() { - print< mlc::switch_<test, char>::ret >(); + print< client::switch_<test, char>::ret >(); } Index: tests/ret.cc --- tests/ret.cc (revision 0) +++ tests/ret.cc (revision 0) @@ -0,0 +1,17 @@ +# include <iostream> +# include <mlc/typedef.hh> + +struct yes +{ + typedef mlc::dummy ret; +}; + +struct no +{ +}; + +int main() +{ + std::cout << mlc::ret_found_in_<yes>::value << std::endl; + std::cout << mlc::ret_found_in_<no>::value << std::endl; +} Index: mlc/ret.hh --- mlc/ret.hh (revision 0) +++ mlc/ret.hh (revision 0) @@ -0,0 +1,63 @@ +// Copyright (C) 2005, 2006 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 59 Temple Place - Suite 330, Boston, +// MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef METALIC_RET_HH +# define METALIC_RET_HH + +# include <mlc/typedef.hh> + + + +/*! \FIXME: what's the correct comment flag here? +** +** Since we often rely on having a typedef named "ret" in types, we offer +** a default equipment for this particular typedef. FIXME: doc +*/ + +mlc_decl_typedef(ret); + +# define mlc_ret(Type) typename typedef_::ret::from_<Type>::ret + + +// test code + +# define mlc_ret2(Type) typename typedef_::ret::from_<Type>::ret2 + + +namespace mlc +{ + + template <typename T> + struct ret_found_in_ + : public mlc::eq_< typename typedef_::ret::from_<T>::ret2::first_elt, + mlc::found > + {}; + +} // end of namespace mlc + + +#endif // ! METALIC_RET_HH Index: mlc/bool.hh --- mlc/bool.hh (revision 414) +++ mlc/bool.hh (working copy) @@ -276,9 +276,8 @@ template <typename bexpr, typename err = no_error_message> struct assert_ : - private virtual internal::check_<bexpr, typename bexpr::is_true> + public virtual internal::check_<bexpr, typename bexpr::is_true> { - typedef dummy is_true; protected: assert_() {} }; @@ -339,6 +338,9 @@ ** each expression is true. 3) using "virtual" allow to encompass ** the multiple base class problem. ** + ** Limitation: no error message can be provided with this present + ** version of multiple_assert_ so prefer using several assert_. + ** ** \see assert_<bexpr, err> */ Index: mlc/flags.hh --- mlc/flags.hh (revision 414) +++ mlc/flags.hh (working copy) @@ -151,6 +151,15 @@ struct not_found : public abstract::flag {}; + /*! \class mlc::found + ** + ** The class corresponding to "not mlc::not_found". + ** + ** \see mlc::not_found + */ + struct found : public abstract::flag {}; + + /*! \class mlc::undefined ** ** Flag class to state that a type is undefined, that is, declared Index: mlc/typedef.hh --- mlc/typedef.hh (revision 414) +++ mlc/typedef.hh (working copy) @@ -1,4 +1,4 @@ -// Copyright (C) 2005 EPITA Research and Development Laboratory +// Copyright (C) 2005, 2006 EPITA Research and Development Laboratory // // This file is part of the Olena Library. This library is free // software; you can redistribute it and/or modify it under the terms @@ -30,6 +30,8 @@ # include <mlc/flags.hh> # include <mlc/bool.hh> +# include <mlc/pair.hh> +# include <mlc/cmp.hh> /*! \macro mlc_decl_typedef(TypedefName) @@ -149,7 +151,7 @@ template <class T> \ static no selector(...); \ \ - template <class T, bool found> \ + template <class T, bool found_> \ struct result; \ \ template <class T> \ @@ -162,6 +164,21 @@ typedef mlc::not_found ret; \ }; \ \ + template <class T, bool found_> \ + struct result2; \ + \ + template <class T> \ + struct result2 <T, true> { \ + typedef mlc::pair_<mlc::found, \ + typename T::TypedefName> ret; \ + }; \ + \ + template <class T> \ + struct result2 <T, false> { \ + typedef mlc::pair_<mlc::not_found, \ + mlc::dummy> ret; \ + }; \ + \ }; \ \ } \ @@ -181,6 +198,10 @@ typedef \ typename helper_::result<T, found_>::ret \ ret; \ + \ + typedef \ + typename helper_::result2<T, found_>::ret \ + ret2; \ }; \ \ template <class T, bool b> \ @@ -205,7 +226,7 @@ \ } \ \ -struct e_n_d__w_i_t_h__s_e_m_i_c_o_l_o_n +struct e_n_d__w_i_t_h__s_e_m_i_c_o_l_o_n \ /*! \macro mlc_typedef(Type, TypedefName) @@ -246,17 +267,4 @@ typedef_::TypedefName::from_onlyif_<Type, mlc_bool(Bexpr)>::ret - -/*! \FIXME: what's the correct comment flag here? -** -** Since we often rely on having a typedef named "ret" in types, we offer -** a default equipment for this particular typedef. FIXME: doc -*/ - -mlc_decl_typedef(ret); - -# define mlc_ret(Type) typename typedef_::ret::from_<Type>::ret - - - #endif // ! METALIC_TYPEDEF_HH Index: mlc/switch.hh --- mlc/switch.hh (revision 414) +++ mlc/switch.hh (working copy) @@ -57,21 +57,6 @@ { }; -// template <typename bexpr> -// struct where_ : public where_<typename bexpr::eval> -// { -// }; - -// template <> -// struct where_<true_> -// { -// }; - -// template <> -// struct where_<false_> -// { -// }; - // FIXME: doc @@ -98,8 +83,8 @@ namespace ERROR { - struct A_case_STATEMENT_IN_mlc_switch_SHOULD_DERIVE_FROM_mlc_where_; - struct A_default_case_STATEMENT_IN_mlc_switch_SHOULD_NOT_DERIVE_FROM_mlc_where_; + struct A_case_STATEMENT_SHOULD_DERIVE_FROM_mlc_where_; + struct A_default_case_STATEMENT_SHOULD_NOT_DERIVE_FROM_mlc_where_; struct SWITCH_DOES_NOT_HAVE_A_CASE_FOR_YOUR_DATA; struct RESULT_IS_NOT_FOUND_IN_default_case_; template <unsigned i> struct RESULT_IS_NOT_FOUND_IN_case_; @@ -134,7 +119,7 @@ undefined), mlc_is_not_a(mlc_comma_1(default_case_<context, data>), where_) >, - ERROR::A_default_case_STATEMENT_IN_mlc_switch_SHOULD_NOT_DERIVE_FROM_mlc_where_ >, + ERROR::A_default_case_STATEMENT_SHOULD_NOT_DERIVE_FROM_mlc_where_ >, private assert_< implies_< mlc_is_not_a(mlc_comma_1(default_case_<context, data>), undefined), @@ -146,8 +131,7 @@ typedef default_case_<context, data> current_t; typedef typename mlc::if_<mlc_is_a(current_t, undefined), none, - current_t>::ret case_type; - typedef mlc_ret(current_t) ret; + current_t>::ret ret; }; @@ -163,12 +147,11 @@ undefined), mlc_is_a(mlc_comma_2(case_<context, data, i>), where_) >, - ERROR::A_case_STATEMENT_IN_mlc_switch_SHOULD_DERIVE_FROM_mlc_where_ > + ERROR::A_case_STATEMENT_SHOULD_DERIVE_FROM_mlc_where_ > { typedef handle_default_case_<context, data> last_t; - typedef typename last_t::case_type case_type; - typedef typename last_t::ret ret; + typedef mlc_ret(last_t) ret; }; @@ -191,8 +174,7 @@ ERROR::RESULT_IS_NOT_FOUND_IN_case_<i> > { - typedef case_<context, data, i> case_type; - typedef mlc_ret(case_type) ret; + typedef case_<context, data, i> ret; }; @@ -216,24 +198,22 @@ internal::case_true)::value, mlc_is_a(mlc_comma_2(case_<context, data, i+1>), internal::case_false)::value > next_t; - typedef typename next_t::case_type case_type; - typedef typename next_t::ret ret; + typedef mlc_ret(next_t) ret; }; - // switch_ + // case_ template <typename context, typename data> - struct switch_ + struct case_ { typedef internal::handle_case_ < context, data, 1, mlc_is_a(mlc_comma_2(case_<context, data, 1>), internal::case_true)::value, mlc_is_a(mlc_comma_2(case_<context, data, 1>), internal::case_false)::value > handle_t; - typedef typename handle_t::case_type case_type; - typedef typename handle_t::ret ret; + typedef mlc_ret(handle_t) ret; }; @@ -244,13 +224,29 @@ // FIXME: doc template <typename context, typename data> - struct switch_ - : private assert_< neq_< mlc_comma_1(typename internal::switch_<context, data>::case_type), + struct case_ + : private assert_< neq_< mlc_comma_1(typename internal::case_<context, data>::ret), none >, ERROR::SWITCH_DOES_NOT_HAVE_A_CASE_FOR_YOUR_DATA > { - typedef typename internal::switch_<context, data>::case_type case_type; - typedef typename internal::switch_<context, data>::ret ret; + typedef typename internal::case_<context, data>::ret ret; + }; + + + // FIXME: doc + + template <typename context, typename data> + struct switch_ + : private assert_< neq_< mlc_comma_1(typename internal::case_<context, data>::ret), + none >, + ERROR::SWITCH_DOES_NOT_HAVE_A_CASE_FOR_YOUR_DATA >, + + private assert_< neq_< mlc_ret(), + not_found >, + ERROR::SWITCH_ + { + typedef typename internal::case_<context, data>::ret case_t; + typedef mlc_ret(case_t) ret; }; Index: mlc/case.hh --- mlc/case.hh (revision 0) +++ mlc/case.hh (revision 0) @@ -0,0 +1,267 @@ +// Copyright (C) 2005, 2006 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 59 Temple Place - Suite 330, Boston, +// MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +#ifndef METALIC_CASE_HH +# define METALIC_CASE_HH + +# include <mlc/bool.hh> +# include <mlc/ret.hh> +# include <mlc/is_a.hh> +# include <mlc/implies.hh> +# include <mlc/comma.hh> +# include <mlc/cmp.hh> +# include <mlc/if.hh> + + +// FIXME: doc this file! + + +namespace mlc +{ + + struct case_selected {}; + struct case_not_selected {}; + + + namespace internal + { + struct a_simple_case; + struct a_switch_case; + + const unsigned unknown_case_id = 10000U; + } + + + namespace ERROR + { + struct A_case_STATEMENT_SHOULD_NOT_START_AT_INDEX_0_BUT_1; + struct A_case_STATEMENT_SHOULD_DERIVE_FROM_mlc_where_; + struct A_default_case_STATEMENT_SHOULD_NOT_DERIVE_FROM_mlc_where_; + struct NO_case_STATEMENT_CAN_BE_SELECTED; + struct A_default_case_STATEMENT_IN_A_switch_SHOULD_HAVE_A_ret; + struct A_case_STATEMENT_IN_A_switch_SHOULD_HAVE_A_ret; + } + + + template <typename bexpr> + struct where_ : public mlc_if_( typename bexpr::eval, + case_selected, + case_not_selected ) + { + }; + + +} // end of namespace mlc + + + + +/// \def mlc_case_equipment_for_namespace(NAMESPACE) + +# define mlc_case_equipment_for_namespace(NAMESPACE) \ + \ +namespace NAMESPACE \ +{ \ + \ + template <typename context, \ + typename data, \ + unsigned i = mlc::internal::unknown_case_id> \ + struct case_ : public mlc::undefined \ + { \ + }; \ + \ + \ + template <typename context, typename data> \ + struct case_ <context, data, 0> : public mlc::undefined \ + { \ + typedef mlc::locked ret; \ + }; \ + \ + \ + template <typename context, \ + typename data> \ + struct default_case_ : public mlc::undefined \ + { \ + }; \ + \ + \ + namespace internal \ + { \ + \ + template <typename use, \ + typename context, typename data, unsigned i, \ + bool the_ith_case_derives_from_true, \ + bool the_ith_case_derives_from_false> \ + struct handle_case_; \ + \ + \ + template <typename use, \ + typename context, typename data, unsigned i> \ + struct handle_case_ <use, context, data, i, 1, 1>; \ + \ + \ + template <typename use, typename context, typename data> \ + struct handle_default_case_ \ + \ + : private mlc::assert_< mlc::implies_< mlc_is_not_a(mlc_comma_1(NAMESPACE::default_case_<context, data>), \ + mlc::undefined), \ + mlc_is_not_a(mlc_comma_1(NAMESPACE::default_case_<context, data>), \ + mlc::where_) >, \ + mlc::ERROR::A_default_case_STATEMENT_SHOULD_NOT_DERIVE_FROM_mlc_where_ >, \ + \ + private mlc::assert_< mlc::implies_< mlc::and_< mlc::eq_<use, mlc::internal::a_switch_case>, \ + mlc_is_not_a(mlc_comma_1(NAMESPACE::default_case_<context, data>), \ + mlc::undefined) >, \ + mlc::ret_found_in_< NAMESPACE::default_case_<context, data> > >, \ + mlc::ERROR::A_default_case_STATEMENT_IN_A_switch_SHOULD_HAVE_A_ret > \ + { \ + typedef NAMESPACE::default_case_<context, data> case_t; \ + typedef typename mlc::if_<mlc_is_a(case_t, mlc::undefined), \ + mlc::none, \ + case_t>::ret ret; \ + }; \ + \ + \ + template <typename use, typename context, typename data, unsigned i> \ + struct handle_case_ <use, context, data, i, 0, 0> \ + \ + : private mlc::assert_< mlc::implies_< mlc_is_not_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::undefined), \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::where_) >, \ + mlc::ERROR::A_case_STATEMENT_SHOULD_DERIVE_FROM_mlc_where_ > \ + { \ + typedef handle_default_case_<use, context, data> handle_t; \ + typedef typename handle_t::ret ret; \ + }; \ + \ + \ + \ + template <typename use, \ + typename context, typename data, unsigned i> \ + struct handle_case_ <use, context, data, i, 1, 0> \ + \ + : private mlc::assert_< mlc::or_< mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::where_), \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::undefined) >, \ + mlc::ERROR::A_case_STATEMENT_SHOULD_DERIVE_FROM_mlc_where_ >, \ + \ + private mlc::assert_< mlc::implies_< mlc::and_< mlc::eq_<use, mlc::internal::a_switch_case>, \ + mlc_is_not_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::undefined) >, \ + mlc::ret_found_in_< NAMESPACE::case_<context, data, i> > >, \ + mlc::ERROR::A_case_STATEMENT_IN_A_switch_SHOULD_HAVE_A_ret > \ + { \ + typedef NAMESPACE::case_<context, data, i> ret; \ + }; \ + \ + \ + \ + template <typename use, \ + typename context, typename data, unsigned i> \ + struct handle_case_ <use, context, data, i, 0, 1> \ + \ + : private mlc::assert_< mlc::or_< mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::where_), \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::undefined) >, \ + mlc::ERROR::A_case_STATEMENT_SHOULD_DERIVE_FROM_mlc_where_ >, \ + \ + private mlc::assert_< mlc::implies_< mlc::and_< mlc::eq_<use, mlc::internal::a_switch_case>, \ + mlc_is_not_a(mlc_comma_2(NAMESPACE::case_<context, data, i>), \ + mlc::undefined) >, \ + mlc::ret_found_in_< NAMESPACE::case_<context, data, i> > >, \ + mlc::ERROR::A_case_STATEMENT_IN_A_switch_SHOULD_HAVE_A_ret > \ + { \ + typedef handle_case_ < \ + use, \ + context, data, i+1, \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i+1>), \ + mlc::case_selected)::value, \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, i+1>), \ + mlc::case_not_selected)::value > next_t; \ + typedef typename next_t::ret ret; \ + }; \ + \ + \ + \ + template <typename use, typename context, typename data> \ + struct select_case_ \ + \ + : private mlc::assert_< mlc::eq_< mlc_ret(mlc_comma_2(NAMESPACE::case_<context, data, 0>)), \ + mlc::locked >, \ + mlc::ERROR::A_case_STATEMENT_SHOULD_NOT_START_AT_INDEX_0_BUT_1 > \ + { \ + typedef handle_case_ < \ + use, \ + context, data, 1, \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, 1>), \ + mlc::case_selected)::value, \ + mlc_is_a(mlc_comma_2(NAMESPACE::case_<context, data, 1>), \ + mlc::case_not_selected)::value > handle_t; \ + typedef typename handle_t::ret ret; \ + }; \ + \ + \ + } \ + \ + \ + template <typename context, typename data> \ + struct case_ <context, data, mlc::internal::unknown_case_id> \ + \ + : private mlc::assert_< mlc::neq_< mlc_comma_2(typename NAMESPACE::internal::select_case_<mlc::internal::a_simple_case, \ + context, data>::ret), \ + mlc::none >, \ + mlc::ERROR::NO_case_STATEMENT_CAN_BE_SELECTED > \ + \ + { \ + typedef typename NAMESPACE::internal::select_case_<mlc::internal::a_simple_case, context, data>::ret ret; \ + }; \ + \ + \ + template <typename context, typename data> \ + struct switch_ \ + \ + : private mlc::assert_< mlc::neq_< mlc_comma_2(typename NAMESPACE::internal::select_case_<mlc::internal::a_switch_case, \ + context, data>::ret), \ + mlc::none >, \ + mlc::ERROR::NO_case_STATEMENT_CAN_BE_SELECTED > \ + \ + { \ + typedef typename NAMESPACE::internal::select_case_<mlc::internal::a_switch_case, context, data>::ret case_t; \ + typedef typename case_t::ret ret; \ + }; \ + \ + \ +} \ + \ +struct e_n_d__w_i_t_h__s_e_m_i_c_o_l_o_n \ + + + +#endif // ! METALIC_CASE_HH
participants (1)
-
Thierry Geraud