Automatic designer selection based on available overloaded versions in Abstract Factory

I am writing an abstract Factory using C ++ templates and was hit by a small hurdle. Namely, the general class T can provide one or more of the following methods of constructing objects:

static T* T::create(int arg); T(int arg); T(); 

I am writing an abstract Factory class so that it can automatically try these three potential constructs in this order:

 template <class T> class Factory { public: T* create(int arg) { return T::create(arg); // first preference return new T(arg); // this if above does not exist return new T; // this if above does not exist // compiler error if none of the three is provided by class T } }; 

How can I achieve this with a C ++ template? Thanks.

+5
source share
2 answers

Something along this line should work:

 struct S { static auto create(int) { return new S; } }; struct T { T(int) {} }; struct U {}; template<int N> struct tag: tag<N-1> {}; template<> struct tag<0> {}; class Factory { template<typename C> auto create(tag<2>, int N) -> decltype(C::create(N)) { return C::create(N); } template<typename C> auto create(tag<1>, int N) -> decltype(new C{N}) { return new C{N}; } template<typename C> auto create(tag<0>, ...) { return new C{}; } public: template<typename C> auto create(int N) { return create<C>(tag<2>{}, N); } }; int main() { Factory factory; factory.create<S>(0); factory.create<T>(0); factory.create<U>(0); } 

It is based on sfinae scheduling methods and tags.
The basic idea is that you pass the create function of your factory to a set of internal functions. These functions check the functions you are looking for due to the tag and are discarded if the test fails. Due to sfinae, if one of them succeeds, the code compiles and everything works as expected.


Here is a similar solution in C ++ 17:

 #include <type_traits> #include <iostream> #include <utility> struct S { static auto create(int) { return new S; } }; struct T { T(int) {} }; struct U {}; template<typename C> constexpr auto has_create(int) -> decltype(C::create(std::declval<int>()), bool{}) { return true; } template<typename C> constexpr auto has_create(char) { return false; } struct Factory { template<typename C> auto create(int N) { if constexpr(has_create<C>(0)) { std::cout << "has create" << std::endl; return C::create(N); } else if constexpr(std::is_constructible_v<C, int>) { std::cout << "has proper constructor" << std::endl; return new C{N}; } else { std::cout << "well, do it and shut up" << std::endl; (void)N; return C{}; } } }; int main() { Factory factory; factory.create<S>(0); factory.create<T>(0); factory.create<U>(0); } 

Thanks to @StoryTeller and @ Jarod42 for their help on this difficult morning.
Look at it and launch wandbox .

+5
source

Ok, thanks answer from @skypjack. I was able to come up with a more compatible solution that works with pre C ++ 11 compilers. The basic idea is the same, i.e. By sending tags for orderly testing. Instead of relying on decltype, I used sizeof and a dummy class for SFINAE.

 struct S { static auto create(int) { return new S; } }; struct T { T(int) {} }; struct U {}; template<class C, int=sizeof(C::create(0))> struct test_1 { typedef int type; }; template<class C, int=sizeof(C(0))> struct test_2 { typedef int type; }; template<class C, int=sizeof(C())> struct test_3 { typedef int type; }; template<int N> struct priority: priority<N-1> {}; template<> struct priority<0> {}; class Factory { template<typename C> C* create(priority<2>, typename test_1<C>::type N) { return C::create(N); } template<typename C> C* create(priority<1>, typename test_2<C>::type N) { return new C(N); } template<typename C> C* create(priority<0>, typename test_3<C>::type N) { return new C(); } public: template<typename C> C* create(int N) { return create<C>(priority<2>(), N); } }; int main() { Factory factory; factory.create<S>(0); factory.create<T>(0); factory.create<U>(0); } 

Not sure if you can even put part of sizeof in the signature of a private function; if so, we can also get rid of dummy classes. (failed). The ugly part is to use constants ( 0 in this case) for the sizeof operator, which can be tricky if the constructors take arguments of very complex types.

0
source

Source: https://habr.com/ru/post/1272651/


All Articles