如果方法不存在,则调用回退方法

时间:2017-06-28 11:54:16

标签: c++ c++11 boost

我希望能够在C ++中执行以下操作

  1. 尝试从命名空间调用函数,例如boost :: filesystem :: copy
  2. 如果copy不是boost :: filesystem的成员,则调用一个回退函数,例如boost :: filesystem3 :: copy
  3. 如果不存在回退功能(这可能是因为boost没有成员filesystem3,或者因为boost :: filesystem3没有成员副本),代码不应该编译。
  4. 在阅读了很长很复杂的代码之后,我不清楚这样做的简单方法是什么。 C ++ 11解决方案很好。但是代码有时需要使用旧的boost版本(1.39.0)进行编译,这正是为什么需要这种解决方法的原因。

    目前我通过在检查BOOST_VERSION宏后创建方法别名来实现。但是,了解更复杂的替代方案可能适用于更一般的情况会很好。

2 个答案:

答案 0 :(得分:2)

这是一个可以解决的问题。
我发现这样做的唯一方法是污染名称空间boost::filesystemboost::filesystem3,然后测试原始函数是否存在。我知道这不是有史以来最好的事情,但是在日常生活中完成并运行是妥协。

两个命名空间中都存在两个copy版本。它们被声明为:

void copy(const path& from, const path& to);
void copy(const path& from, const path& to, system::error_code& ec);

请注意,我在下面的示例中使用稍微不同的形式重新声明了它们,以简化操作并避免在示例代码中使用boost。

这是一个最小的工作示例:

#include<iostream>
#include<type_traits>

// original namespaces

namespace boost { namespace filesystem {
    void copy(int, char) { std::cout << "b::f::copy" << std::endl; }
    void copy(int, char, double) {}

    // ... everything else ...
}}

namespace boost { namespace filesystem3 {
    void copy(int, char) { std::cout << "b::f3::copy" << std::endl; }
    void copy(int, char, double) {}

    // ... everything else ...
}}

// pollution

namespace boost { namespace filesystem {
    struct tag {};
    void copy(tag, tag) {}
}}

namespace boost { namespace filesystem3 {
    struct tag {};
    void copy(tag, tag) {}
}}

std::true_type test(int, void(*)(int, char));
std::false_type test(...);

constexpr bool has_filesystem_copy = decltype(test(0, &boost::filesystem::copy))::value;
constexpr bool has_filesystem3_copy = decltype(test(0, &boost::filesystem3::copy))::value;

template<bool = true>
struct fallback_fn {};

template<>
struct fallback_fn<has_filesystem3_copy> {
    template<typename... Args>
    static void invoke(Args... args) {
        boost::filesystem3::copy(args...);
    }
};

template<bool = true>
struct copy_fn: fallback_fn<> {};

template<>
struct copy_fn<has_filesystem_copy> {
    template<typename... Args>
    static void invoke(Args... args) {
        boost::filesystem::copy(args...);
    }
};

int main() {
    copy_fn<>::invoke(0, 'c');
}

随意使用标记为原始名称空间的名称空间中的函数。
总结一下:

  • 如果copyboost::filesystem同时提供boost::filesystem3,则会选择前者。请参阅wandbox

  • 如果copy仅在boost::filesystem中可用,则会将其取消。请参阅wandbox

  • 如果copy仅在boost::filesystem3中可用,则会将其取消。请参阅wandbox

  • 如果copy根本不可用,则会出现如下编译时错误:

      

    'invoke'不是'copy_fn&lt;&gt;'

    的成员

    wandbox上查看。

为此,我使用了模板特化规则和几个constexpr变量 请注意,如果您愿意,可以避免包含<type_traits>

constexpr bool test(int, void(*)(int, char)) { return true; }
constexpr bool test(...) { return false; }

constexpr bool has_filesystem_copy = test(0, &boost::filesystem::copy);
constexpr bool has_filesystem3_copy = test(0, &boost::filesystem3::copy);

同样,污染命名空间并不是你能想到的最好的想法。无论如何,只要你通过像copy这样的实用程序类调用copy_fn,它就可以在这种情况下起作用。 作为旁注,请记住,如果您必须 wrap 多个函数,那么它非常烦人并且容易出错。如果我只查看你问题的文字,情况并非如此,但我不知道真实情况是什么。

答案 1 :(得分:1)

这里的另一个想法是(大部分)诀窍:

storm.zookeeper.servers:
   - "10.0.xx.xxx"

 storm.zookeeper.port: 2181

 nimbus.seeds: ["10.0.xxx.xxx"]

 storm.local.dir: "/storm/datadir"

一些警告:命名空间必须实际存在,但您始终可以将它们定义为空。测试的函数不得存在于具有兼容参数的全局范围内。特别是,您无法将我的上一个#include <tuple> #include <utility> #include <iostream> namespace fake_boost { namespace filesystem { // class path { public: // template<typename Source> path(Source const&) {} // }; // void copy( const path&, const path& ) // { std::cout << "fake_boost::filesystem::copy\n"; } } namespace filesystem3 { class path { public: template<typename Source> path(Source const&) {} }; void copy( const path&, const path& ) { std::cout << "fake_boost::filesystem3::copy\n"; } } } namespace test_copy { template <typename...> using void_t = void; // or use C++17 std::void_t namespace test_filesystem3 { using namespace fake_boost::filesystem3; template <typename... Args> void do_copy(Args&& ... args) { copy(std::forward<Args>(args)...); } } namespace test_filesystem { template <typename Tuple, typename Enable=void> struct copy_switcher { template <typename... Args> static void do_copy(Args&& ... args) { test_filesystem3::do_copy(std::forward<Args>(args)...); } }; using namespace fake_boost::filesystem; template <typename... Args> struct copy_switcher<std::tuple<Args...>, void_t<decltype(copy(std::declval<Args&&>()...))>> { static void do_copy(Args&& ... args) { copy(std::forward<Args>(args)...); } }; } } template <typename... Args> void do_copy(Args&& ... args) { test_copy::test_filesystem::copy_switcher<std::tuple<Args...>> ::do_copy(std::forward<Args>(args)...); } int main() { do_copy( "from.txt", "to.txt" ); } 重命名为do_copy