Der große C++11 TMP-Contest!



  • Hey Leute, in diesem Thread könnt ihr mal eure abgefahrensten (bzw nutzlosesten) Template-Metaprogramme zeigen. Regeln: So gut wie keine, außer dass es gültiges C++11 ist.
    Ich fange an: Darstellung rationaler Zahlen und Berechnung von Wurzeln u.a. zur Compile-Time:

    #include <iostream>
    
    namespace tmp {
    
    	template <unsigned N>
    	struct fac { static constexpr long long int value = typename fac<N - 1>::value * N; };
    
    	template <>
    	struct fac<0> { static constexpr long long int value = 1; };
    
    	template <long long int L, long long int R>
    	struct plus { static constexpr long long int value = L + R; };
    
    	template <long long int L, long long int R>
    	struct minus { static constexpr long long int value = L - R; };
    
    	template <long long int, long long int>
    	struct mul { };
    
    	template <long long int, long long int>
    	struct div { };
    
    	template <long long int A, long long int B>
    	struct euklid { static constexpr long long int value = euklid<B, A % B>::value; };
    
    	template <long long int A>
    	struct euklid<A, 0> { static constexpr long long int value = A; };
    
    	template <long long int Nominator, long long int Denominator, typename T = double>
    	struct rational {
    		typedef T type;
    		static constexpr long long int nominator = Nominator,
    			denominator = Denominator;
    		static constexpr T value() { return Nominator / T(Denominator); }
    	};
    
    	template <template <long long int, long long int> class op, typename L, typename R>
    	struct op_dispatcher {
    		typedef rational<op<R::nominator * L::denominator, R::denominator * L::nominator>::value, R::denominator * L::denominator> type;
    	};
    
    	template <typename L, typename R>
    	struct op_dispatcher<mul, L, R> {
    		typedef rational<R::nominator * L::nominator, R::denominator * L::denominator> type;
    	};
    
    	template <typename L, typename R>
    	struct op_dispatcher<div, L, R> {
    		typedef rational<L::nominator * R::denominator, L::denominator * R::nominator> type;
    	};
    
    	template <template <long long int, long long int> class op, typename L, typename R>
    	class rational_op {
    		typedef typename op_dispatcher<op, L, R>::type c_;
    		static constexpr long long int gcd_ = euklid<c_::nominator, c_::denominator>::value;
    	public:
    		typedef rational<c_::nominator / gcd_, c_::denominator / gcd_> type;
    	};
    
    	template <typename L, typename R>
    	struct rational_add { typedef typename rational_op<plus, L, R>::type type; };
    
    	template <typename L, typename R>
    	struct rational_sub { typedef typename rational_op<minus, L, R>::type type; };
    
    	template <typename L, typename R>
    	struct rational_mul { typedef typename rational_op<mul, L, R>::type type; };
    
    	template <typename L, typename R>
    	struct rational_div { typedef typename rational_op<div, L, R>::type type; };
    
    	template <typename B, unsigned N>
    	struct rational_pow {
    		typedef typename rational_mul<typename rational_pow<B, N - 1>::type, B>::type type;
    	};
    
    	template <typename B>
    	struct rational_pow<B, 0> {
    		typedef rational<1, 1> type;
    	};
    
    	template <unsigned X, unsigned N = 4, typename T = double>
    	struct sqroot { // heron!
    		template <unsigned NN, unsigned XX, typename TT>
    		struct iteration {
    			typedef typename rational_div
    				<
    					typename rational_add
    						<
    							typename iteration<NN - 1, XX, TT>::type,
    							typename rational_div<rational<XX, 1>, typename iteration<NN - 1, XX, TT>::type>::type
    						>::type,
    					rational<2, 1>
    				>::type type;
    		};
    
    		template <unsigned XX, typename TT>
    		struct iteration<1, XX, TT> {
    			typedef rational<XX + 1, 2, TT> type;
    		};
    
    		typedef typename iteration<N, X, T>::type type;
    
    		static constexpr T value() { return type::value(); }
    	};
    
    	typedef rational_pow<rational<15 + 1, 15>, 15>::type e; // e := lim ((n+1)/n)^n as n -> infinity
    
    }
    
    int main() {
    	using namespace tmp;
    	std::cout << sqroot<2>::value() << std::endl;
    }
    

    Ich freue mich auf den Template-Brainfuck!



  • Mit C wär das nicht passiert.



  • C Coder schrieb:

    Mit C wär das nicht passiert.

    👍



  • Mit D wäre das normal lesbar.



  • Ich packe noch einen drauf: natürlicher Logarithmus:

    template <unsigned X, unsigned N = 5, typename T = double>
    struct ln {
    	template <unsigned XX, unsigned NN, typename TT>
    	struct iteration {
    		typedef typename rational_add
    			<
    				typename iteration<XX, NN - 2, TT>::type,
    				typename rational_mul
    					<
    						rational<1, NN, TT>,
    						typename rational_pow<rational<XX - 1, XX + 1, TT>, NN>::type
    					>::type
    			>::type type;
    	};
    
    	template <unsigned XX, typename TT>
    	struct iteration<XX, 1, TT> {
    		typedef rational<XX - 1, XX + 1, TT> type;
    	};
    
    	typedef typename iteration<X, N, T>::type type;
    
    	static constexpr T value() { return 2 * type::value(); }
    };
    
    ...
    
    std::cout << ln<3>::value() << std::endl;
    

    Kann das jemand ändern, sodass auch sowas wie ln<rational<2, 3>>::value() funktioniert? 😃



  • Vorschlag:

    template <typename X, unsigned N = 5, typename T = double>
    struct rational_ln {
    	template <typename XX, unsigned NN, typename TT>
    	struct iteration {
    		typedef typename rational_add
    			<
    				typename iteration<XX, NN - 2, TT>::type,
    				typename rational_mul
    					<
    						rational<1, NN, TT>,
    						typename rational_pow
    							<
    								typename rational_div
    									<
    									typename rational_sub<XX, rational<1, 1>>::type,
    										typename rational_add<XX, rational<1, 1>>::type
    									>::type,
    								NN
    							>::type
    					>::type
    			>::type type;
    	};
    
    	template <typename XX, typename TT>
    	struct iteration<XX, 1, TT> {
    		typedef typename rational_div
    			<
    				typename rational_sub<XX, rational<1, 1>>::type,
    				typename rational_add<XX, rational<1, 1>>::type
    			>::type type;
    	};
    
    	typedef typename iteration<X, N, T>::type type;
    
    	static constexpr T value() { return 2 * type::value(); }
    };
    

    Allerdings verbasselt er manchmal das Vorzeichen, weiß jemand, warum?





  • Ok, an dem hier arbeite ich gerade noch:

    #include <cstddef>
    #include <type_traits>
    #include <memory>
    #include <cassert>
    
    #include <iostream>
    #include <limits>
    
    #include "type_list.hxx"
    #include "index_list.hxx"
    
    using namespace vtmpl;
    
    /// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    template< typename, typename > struct best_conv_impl;
    
    template< typename S, typename ... Args >
    struct best_conv_impl<S, type_list<Args...>> :
    	best_conv_impl< S,
    	                type_list< typename best_conv_impl<S, eval<sub_list<type_list<Args...>, 0, (sizeof...(Args)+1)/2>> >::closest,
    	                           typename best_conv_impl<S, eval<sub_list<type_list<Args...>,    (sizeof...(Args)+1)/2>> >::closest > > {};
    
    template< typename S, typename T, typename T2 >
    struct best_conv_impl<S, type_list<T, T2>>
    {
    	static T  f(T );
    	static T2 f(T2);
    	static void f( ... );
    
    	using closest = decltype(f(std::declval<S>()));
    };
    
    template< typename S, typename T >
    struct best_conv_impl<S, type_list<void, T>> : best_conv_impl<S, type_list<T>> {};
    template< typename S, typename T >
    struct best_conv_impl<S, type_list<T, void>> : best_conv_impl<S, type_list<T>> {};
    
    template< typename S, typename T >
    struct best_conv_impl<S, type_list<T>>
    {
    	static T  f(T );
    	static void f( ... );
    
    	using closest = decltype(f(std::declval<S>()));
    };
    
    template< typename S >
    struct best_conv_impl<S, type_list<void>>
    { using closest = void; };
    
    template< typename S, typename ... Args >
    using best_implicit_conversion = typename best_conv_impl<S, type_list<Args...>>::closest;
    
    /// //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    template< typename T, typename = typename /*vtmpl::*/make_index_list<T::length>::type > class variadic_variant;
    
    struct bad_get : std::invalid_argument
    {
    	using std::invalid_argument::invalid_argument;
    };
    
    struct WrapperBase
    {
    	std::size_t index;
    
    	WrapperBase( std::size_t i ) : index{i} {}
    
    	virtual WrapperBase& operator=( WrapperBase const& ) = 0;
    	//virtual bool operator==( WrapperBase const& ) = 0;
    	virtual ~WrapperBase() = default;
    };
    
    template< typename ... Types, index_type ... Indices >
    class variadic_variant</*vtmpl::*/type_list<Types...>, /*vtmpl::*/index_list<Indices...>>
    {
    	template<typename, typename>
    	friend class variadic_variant;
    
    	static_assert( sizeof...(Types) != 0, "No types provided!" );
    
    	using List = /*vtmpl::*/type_list<Types...>;
    
    	static std::reference_wrapper<std::type_info const> _TypeInfos[];
    
    	template< typename U, bool check = true >
    	struct _find
    	{
    		using U_nonconst = /*vtmpl::*/eval<std::remove_const<U>>;
    
    		static constexpr auto pos_cq   = /*vtmpl::*/find<List, U         >::value,
    		                      pos_nocq = /*vtmpl::*/find<List, U_nonconst>::value;
    
    		static constexpr auto pos = pos_cq != /*vtmpl::*/npos? pos_cq : pos_nocq != /*vtmpl::*/npos? pos_nocq : /*vtmpl::*/npos;
    
    		static_assert( !check || pos != /*vtmpl::*/npos, "Requesting type not hold by variant!" );
    	};
    
    	template< typename T >
    	struct _Wrapper : WrapperBase
    	{
    		T data;
    
    		_Wrapper& operator=( WrapperBase const& o )
    		{
    			assert( WrapperBase::index == o.index );
    			data = static_cast<_Wrapper const&>(o).data;
    			return *this;
    		}
    
    		template< typename ... Args >
    		_Wrapper( Args&&... args ) :
    			WrapperBase{ /*vtmpl::*/find<List, T>::value },
    			data( std::forward<Args>(args)... ) {}
    	};
    
    	template< typename T, typename...  Args >
    	static auto _makeWrapper( Args&&... args )
    	{
    		return std::unique_ptr<WrapperBase>{ new _Wrapper<T>( std::forward<Args>(args)... ) };
    	}
    
    	template< std::size_t I, typename...  Args >
    	static auto _makeWrapper( Args&&... args )
    	{
    		return _makeWrapper<type_list_at<List, I>>( std::forward<Args>(args)... );
    	}
    
    	template< typename T >
    	static auto _copy( WrapperBase* p )
    	{
    		return std::unique_ptr<WrapperBase>{ new _Wrapper<T>( static_cast<_Wrapper<T>*>(p)->data ) };
    	}
    
    	template< typename ... T >
    	static auto _createByIndex( std::size_t I, T&&... CArgs )
    	{
    		static std::unique_ptr<WrapperBase>(*arr[])(T&&...) { _makeWrapper<Types, T...>... };
    		return arr[I]( std::forward<T>(CArgs)... );
    	}
    
    	static auto _copyByIndex( std::size_t I, WrapperBase* p )
    	{
    		static std::unique_ptr<WrapperBase>(*arr[])(WrapperBase*) { _copy<Types>... };
    		return arr[I](p);
    	}
    
    	std::unique_ptr<WrapperBase> mWrapPtr{ new _Wrapper</*vtmpl::*/type_list_at<List, 0>> };
    
    public:
    
    	std::size_t which() const { return mWrapPtr->index; }
    
    	std::type_info const& type() const { return _TypeInfos[which()]; }
    
    	void swap( variadic_variant& other )
    	{
    		std::swap( mWrapPtr, other.mWrapPtr );
    	}
    
    	variadic_variant() {} /// no =default because of Clang bug:
    	                      // requires "user-provided default constructor" for const objects
    
    	variadic_variant( variadic_variant const& v ) :
    		mWrapPtr{ _copyByIndex( v.mWrapPtr->index, v.mWrapPtr.get() ) } {}
    
    	variadic_variant& operator=( variadic_variant const& v )
    	{
    		if( v.which() == which() )
    			*mWrapPtr = *v.mWrapPtr;
    		else
    			mWrapPtr = _copyByIndex( v.mWrapPtr->index, v.mWrapPtr.get() );
    
    		return *this;
    	}
    
    	variadic_variant( variadic_variant&& ) = default;
    	variadic_variant& operator=( variadic_variant&& ) = default;
    
    	template< typename T, typename = requires<!std::is_same<best_implicit_conversion<T, Types...>, void>::value> >
    	variadic_variant( T&& o ) :
    		mWrapPtr{ _makeWrapper<find< List, best_implicit_conversion<T, Types...> >::value>( std::forward<T>(o) ) }
    	{}
    
    	template< typename T, typename = requires<find< List, best_implicit_conversion<T, Types...> >::value != npos> >
    	variadic_variant& operator=( T&& o )
    	{
    		static constexpr auto pos = find< List, best_implicit_conversion<T, Types...> >::value;
    
    		if( which() == pos )
    			static_cast<_Wrapper<type_list_at<List, pos>>&>(*mWrapPtr.get()).data = std::forward<T>(o);
    		else
    			mWrapPtr = _makeWrapper<pos>( std::forward<T>(o) );
    
    		return *this;
    	}
    
    	template< typename OList >
    	variadic_variant( variadic_variant<OList> const& o ) :
    		mWrapPtr{}
    	{
    		static std::unique_ptr<WrapperBase>(*arr[])(WrapperBase*) =
    		{  []( WrapperBase* p ){ return _makeWrapper<best_implicit_conversion<type_list_at<OList, Indices>, Types...>>( static_cast<_Wrapper<type_list_at<OList, Indices>>&>(*p).data ); } ... };
    		mWrapPtr = arr[o.which()](o.mWrapPtr.get());
    	}
    
    	template<typename U>
    	friend U const* get( variadic_variant const* o )
    	{
    		static constexpr auto pos = _find<U>::pos;
    
    		if( pos != o->which() )
    			return nullptr;
    
    		auto ptr = static_cast<_Wrapper</*vtmpl::*/cond<_find<U>::pos_cq != /*vtmpl::*/npos, U, typename _find<U>::U_nonconst>>*>( o->mWrapPtr.get() );
    
    		return &ptr->data;
    	}
    
    	template< typename U > void operator==( U const& ) = delete;
    	template< typename U > void operator< ( U const& ) = delete;
    
    	bool operator==( variadic_variant const& other )
    	{
    		return which() == other.which();
    	}
    
    	bool operator< ( variadic_variant const& other )
    	{
    		return which() < other.which();
    	}
    
    };
    
    template<typename U, typename ... Args>
    U* get( variadic_variant<Args...>* o )
    {
    	return const_cast<U*>( get<U>(static_cast<variadic_variant<Args...> const*>(o)) );
    }
    
    template<typename U, typename ... Args>
    U& get( variadic_variant<Args...>& o )
    {
    	auto p = get<U>(&o);
    
    	if( !p )
    		throw bad_get{"get(variadic_variant&) called with invalid type"};
    
    	return *p;
    }
    
    template<typename U, typename ... Args>
    U const& get( variadic_variant<Args...> const& o )
    {
    	auto p = get<U>(&o);
    
    	if( !p )
    		throw bad_get{"get(variadic_variant const&) called with invalid type"};
    
    	return *p;
    }
    
    template< typename ... Types, index_type... Indices >
    std::reference_wrapper<std::type_info const> variadic_variant<type_list<Types...>, index_list<Indices...>>::_TypeInfos[]{ typeid(Types)... };
    
    template<typename... Args>
    void swap( variadic_variant<Args...>& lhs, variadic_variant<Args...>& rhs )
    {
    	lhs.swap(rhs);
    }
    

    Natürlich stelle ich auch noch meine Lib VTMPL mit rein (auf Github zu finden).



  • @HeronDerGroße: Die typename ...::type Konstrukte ersetzt du am Besten durch ein entsprechendes Alias-Template wie eval:

    template< typename T >
    using eval = typename T::type;
    

    Den natürlichen Logarithmus kann man sicher durch eine einfache constexpr -Funktion ersetzen, die einen Kettenbruch o.ä. berechnet.

    Edit: Möchtest du den Titel abändern zu C++14 TMP Contest?



  • Arcoth schrieb:

    @HeronDerGroße: Die typename ...::type Konstrukte ersetzt du am Besten durch ein entsprechendes Alias-Template wie eval:

    template< typename T >
    using eval = typename T::type;
    

    Hab ich, damit sieht der Code fast menschlich aus.

    Arcoth schrieb:

    Den natürlichen Logarithmus kann man sicher durch eine einfache constexpr -Funktion ersetzen, die einen Kettenbruch o.ä. berechnet.

    Vielleicht. Aber ich will den Logarithmus als Typ und nicht als Wert, da bringt mir eine Funktion garnichts.

    Hier der neue Code (mit rationaler Wurzel und Logarithmen zu verschiedenen Basen):

    #include <iostream>
    
    namespace tmp {
    
    	template <unsigned N>
    	struct fac { static constexpr auto value = fac<N - 1>::value * N; };
    
    	template <>
    	struct fac<0> { static constexpr long long int value = 1; };
    
    	template <long long int L, long long int R>
    	struct plus { static constexpr auto value = L + R; };
    
    	template <long long int L, long long int R>
    	struct minus { static constexpr auto value = L - R; };
    
    	template <long long int, long long int>
    	struct mul { };
    
    	template <long long int, long long int>
    	struct div { };
    
    	template <long long int A, long long int B>
    	struct euklid { static constexpr auto value = euklid<B, A % B>::value; };
    
    	template <long long int A>
    	struct euklid<A, 0> { static constexpr auto value = A; };
    
    	template <long long int Nominator, long long int Denominator, typename T = double>
    	struct rational {
    		typedef T type;
    		static constexpr long long int nominator = Nominator,
    									   denominator = Denominator;
    		static constexpr T value() { return Nominator / T(Denominator); }
    	};
    
    	template <template <long long int, long long int> class op, typename L, typename R>
    	struct op_dispatcher {
    		typedef rational<op<R::nominator * L::denominator, R::denominator * L::nominator>::value, R::denominator * L::denominator> type;
    	};
    
    	template <typename L, typename R>
    	struct op_dispatcher<mul, L, R> {
    		typedef rational<R::nominator * L::nominator, R::denominator * L::denominator> type;
    	};
    
    	template <typename L, typename R>
    	struct op_dispatcher<div, L, R> {
    		typedef rational<L::nominator * R::denominator, L::denominator * R::nominator> type;
    	};
    
    	template <template <long long int, long long int> class op, typename L, typename R>
    	class rational_op {
    		typedef typename op_dispatcher<op, L, R>::type c_;
    		static constexpr long long int gcd_ = euklid<c_::nominator, c_::denominator>::value;
    	public:
    		typedef rational<c_::nominator / gcd_, c_::denominator / gcd_> type;
    	};
    
    	template <typename L, typename R>
    	using rational_add = typename rational_op<plus, L, R>::type;
    
    	template <typename L, typename R>
    	using rational_sub = typename rational_op<minus, L, R>::type;
    
    	template <typename L, typename R>
    	using rational_mul = typename rational_op<mul, L, R>::type;
    
    	template <typename L, typename R>
    	using rational_div = typename rational_op<div, L, R>::type;
    
    	template <typename B, unsigned N>
    	struct rational_pow_op {
    		typedef rational_mul<typename rational_pow_op<B, N - 1>::type, B> type;
    	};
    
    	template <typename B>
    	struct rational_pow_op<B, 0> {
    		typedef rational<1, 1> type;
    	};
    
    	template <typename B, unsigned N>
    	using rational_pow = typename rational_pow_op<B, N>::type;
    
    	template <unsigned long long X, unsigned N, typename T = double>
    	struct sqroot_op {
    		typedef rational_div
    			<
    				rational_add
    					<
    						typename sqroot_op<X, N - 1, T>::type,
    						rational_div<rational<X, 1>, typename sqroot_op<X, N - 1, T>::type>
    					>,
    				rational<2, 1>
    			>type;
    	};
    
    	template <unsigned long long X, typename T>
    	struct sqroot_op<X, 1, T> {
    		typedef rational<X + 1, 2, T> type;
    	};
    
    	template <unsigned long long X, unsigned N = 4, typename T = double>
    	using sqroot = typename sqroot_op<X, N, T>::type;
    
    	template <typename X, unsigned N, typename T = typename X::type>
    	struct rational_sqroot_op {
    		typedef rational_div
    			<
    				rational_add
    					<
    						typename rational_sqroot_op<X, N - 1, T>::type,
    						rational_div<X, typename rational_sqroot_op<X, N - 1, T>::type>
    					>,
    				rational<2, 1, T>
    			> type;
    	};
    
    	template <typename X, typename T>
    	struct rational_sqroot_op<X, 1, T> {
    		typedef rational_div<rational_add<X, rational<1, 1, T>>, rational<2, 1, T>> type;
    	};
    
    	template <typename X, unsigned N = 4, typename T = typename X::type>
    	using rational_sqroot = typename rational_sqroot_op<X, N, T>::type;
    
    	typedef rational_pow<rational<15 + 1, 15>, 15> e; // e := lim ((n+1)/n)^n as n -> infinity
    
    	template <unsigned long long X, unsigned N, typename T = double>
    	struct ln_op {
    		typedef rational_add
    			<
    				typename ln_op<X, N - 2, T>::type,
    				rational_mul
    					<
    						rational<1, N, T>,
    						rational_pow<rational<X - 1, X + 1, T>, N>
    					>
    			> type;
    	};
    
    	template <unsigned long long X, typename T>
    	struct ln_op<X, 1, T> {
    		typedef rational<X - 1, X + 1, T> type;
    	};
    
    	template <unsigned long long X, unsigned N = 5, typename T = double>
    	using ln = typename ln_op<X, N, T>::type;
    
    	template <unsigned long long X, unsigned long long B, unsigned N, typename T = double>
    	struct log_op {
    		typedef rational_div<ln<X, N, T>, ln<B, N, T>> type;
    	};
    
    	template <unsigned long long X, unsigned long long B, unsigned N = 5, typename T = double>
    	using log = typename log_op<X, B, N, T>::type;
    
    	template <typename X, unsigned N, typename T = double>
    	struct rational_ln_op {
    			typedef rational_add
    				<
    					typename rational_ln_op<X, N - 2, T>::type,
    					rational_mul
    						<
    							rational<1, N, T>,
    							rational_pow
    								<
    									rational_div
    										<
    											rational_sub<X, rational<1, 1>>,
    											rational_add<X, rational<1, 1>>
    										>,
    									N
    								>
    						>
    				> type;
    	};
    
    	template <typename X, typename T>
    	struct rational_ln_op<X, 1, T> {
    		typedef rational_div<rational_sub<X, rational<1, 1>>,rational_add<X, rational<1, 1>>> type;
    	};
    
    	template <typename X, unsigned N = 5, typename T = double>
    	using rational_ln = rational_mul<typename rational_ln_op<X, N, T>::type, rational<-2, 1>>;
    
    	template <typename X, typename B, unsigned N, typename T = double>
    	struct rational_log_op {
    		typedef rational_div<rational_ln<X, N, T>, rational_ln<B, N, T>> type;
    	};
    
    	template <typename X, typename B, unsigned N = 5, typename T = double>
    	using rational_log = typename rational_log_op<X, B, N, T>::type;
    
    }
    
    int main() {
    	using namespace tmp;
    	std::cout << rational_pow<rational<1, 2>, 2>::value() << std::endl;
    	std::cout << sqroot<2>::value() << ' ' << rational_sqroot<rational<7, 5>>::value() << std::endl;
    	std::cout << rational_ln<rational<2, 3>>::value() << ' ' << ln<2>::value() << ' ' << sqroot<2>::value() << std::endl;
    	std::cout << tmp::log<7, 3>::value() << ' ' << rational_log<rational<2, 3>, rational<7, 2>>::value() << std::endl;
    }
    

    Das kompiliert sogar im Visual Studio (November '13 CTP, though).

    Arcoth schrieb:

    Edit: Möchtest du den Titel abändern zu C++14 TMP Contest?

    Kann ich nicht.



  • Bevor ich ihn lese, was macht dein Code? Anwendungsbeispiel?


  • Mod

    HeronDerGroße schrieb:

    Anwendungsbeispiel?

    Teilnahme an TMP-Wettbewerben. 😃



  • Aber ich will den Logarithmus als Typ und nicht als Wert, da bringt mir eine Funktion garnichts.

    Schon mal daran gedacht, dass du den Wert in ein Template einsetzen kannst? Außer natürlich, du bestehst auf einen Bruch.

    SeppJ schrieb:

    Teilnahme an TMP-Wettbewerben. 😃

    👍


Log in to reply