草庐IT

c++ - 如何为任意(但在编译时定义)功能支持定义抽象基类?

coder 2024-02-25 原文

我有以下C++设计问题,非常感谢任何建议/解决方案。
请注意,我的背景不是计算机科学,因此我可能缺少一些明显的解决方案。

我通常在代码中分离关键组件的方式是通过抽象类和纯虚函数定义接口(interface)。

范例1:

class B
{
public:
    virtual double f( double x ) = 0;
};

class D1 : public B
{
public:
    double f( double x ) const 
    {
    return 0.0;
    }
};    

class D2 : public B
{    
public:
    double f( double x ) const 
    {
    return 1.0;
    }
};

这样我就可以很好地将接口(interface)与实现分开。
这种方法也非常快(而且由于我正在研究的是数字库,所以这很重要:P)。

现在,我面临的问题是以下问题。

我有一组“功能”,可以通过功能(在下面定义)f(),g()和h()进行总结。
请注意,所有这些函数的参数和返回类型通常都不同。

假设我有一些代码需要一个指向实现功能f()和g()的对象的指针。
我想做的是能够传递具有“更多或相等”功能的东西,例如支持f(),g()和h()的东西。

为了更好地解释自己,这里是一些代码。
请注意,我可以使用“嵌套初始化”方法来代替多重继承,例如在boost::operators中。这里的要点是,我永远不会遇到f()与g()相同的情况。所有功能都不同。
问题是,为了完成这项工作,我需要使用reinterpret_cast,如下面的示例所示(因此,这并不是真正的解决方案):

范例2:
class F {
public:
    virtual double f( double x ) = 0;
};

class G {
public:
    virtual double g( double x ) = 0;
};

class H {
public:
    virtual double h( double x ) = 0;
};

class N {};

template<class T1, class T2=N, class T3=N>
class Feature : public T1 , public T2 , public T3
{
};

template<class T1, class T2>
class Feature<T1,T2,N> : public T1, public T2
{
};

template<class T1>
class Feature<T1,N,N> : public T1
{
};

//Supp for Supports/Implements
class SuppFandG : public Feature<F,G>
{
public:
    double f( double x ) { return 0.0; }
    double g( double x ) { return 1.0; } 
};

class SuppFandH : public Feature<F,H>
{
public:
    double f( double x ) { return 0.0; }
    double h( double x ) { return 1.0; } 
};

class SuppFandGandH : public Feature<F,G,H>
{
public:
    double f( double x ) { return 0.0; }
    double g( double x ) { return 1.0; }
    double h( double x ) { return 2.0; }
};

int main()
{
    Feature<F,G>* featureFandGPtr;
    Feature<F,H>* featureFandHPtr;
    Feature<H,F>* featureHandFPtr;
    Feature<F,G,H>* featureFandGandHPtr;

    SuppFandGandH suppFandGandH;
    featureFandGandHPtr = &suppFandGandH;

    //featureFandGPtr = featureFandGandHPtr; //Illegal. static_cast illegal too.
    //the reason to do this is that I would like to pass a pointer to an object 
    //of type Feature<F,G,H> to a function (or constructor) that expects a pointer to Feature<F,G> 
    featureFandGPtr = reinterpret_cast< Feature<F,G>* >( featureFandGandHPtr );
    featureFandHPtr = reinterpret_cast< Feature<F,H>* >( featureFandGandHPtr );
    featureHandFPtr = reinterpret_cast< Feature<H,F>* >( featureFandGandHPtr );

    featureFandGPtr->f( 1.0 );
    featureFandGandHPtr->h( 1.0 );
}

或者,我可以尝试构造继承层次结构,但更改Feature的定义,但是以下示例使Visual Studio 2008专业版编译器崩溃,因此无法对其进行测试。

范例3:
//This will not work, Visual studio 2008 professional crash.
template<class T1, class T2=N, class T3=N>
class Feature : public Feature<T1,T2> , public Feature<T1,T3> , public Feature<T2,T3>
{
};

template<class T1, class T2>
class Feature<T1,T2,N> : public Feature<T1>, public Feature<T2>
{
};

template<class T1>
class Feature<T1,N,N> : public T1
{
};

用这种方法我仍然有问题
1)Feature在功能上(对于我想要实现的功能)等效于Feature,但是它们的类型不同。
但是,这可以通过使用MPL boost库(总是对类型进行“排序”)进行一些花哨的元编程来解决,因此为简单起见,我们假设这不是问题。

2)存在多个基础的问题,我想避免通过虚拟基础进行虚拟继承(性能下降)。
通过使用Feature专长中的指令,可以解决此问题。

不过,我还是不能100%地确定是否可以进行这项工作,因此无法针对大量功能进行很好的扩展。
实际上,构成层次结构的元素数量由二项式系数(几乎是阶乘)给出:
F-> F(1)
F,G-> FG,F,G(3)
F,G,H-> FGH,FG,GH,FH,F,G,H(7)

我想知道是否存在涉及以下条件的设计问题的解决方案:

1)代码应具有与示例1相同的运行时性能。

2)我希望能够轻松地指定一些功能集,并能够“传递”指向具有此(通常是额外的)功能的对象的指针。

3)每当我在别处考虑新功能h()时,我都希望依赖功能f()和g()的代码不需要重新编译。

4)我不想模板化所有想要使用这些功能的东西(几乎所有代码)。应该有某种“分离”,请参见第3点。

在(数字)库中查看时,通常会发现两种方法:

1)定义一个巨大的抽象基类B,它具有f(),g(),h(),......
问题:每当我想添加新功能z()时,都必须修改B,所有内容都需要重新编译(即使此代码根本不关心z()),所有现有的实现D1,D2 B的,...需要修改(通常是通过让它们分别抛出z()的异常,以支持z()的新实现)。
当我需要添加特征时,逐步放大B的解决方案并不是解决当前问题的好方法,因为特征f()和g()实际上与h()和i()一样“重要”,两者都不比其他人“更基础”。

2)分离所有功能,并为每个功能使用一个指针。
但是,这对于用户来说很麻烦(在大多数情况下,必须携带4个或更多的指针),而且对于手头的问题,这种方法不是最佳的(这里实际上是1个对象可能会或可能不会做某事,实际上,调用f()会修改g()获得的结果,反之亦然。

预先感谢您的帮助。

克劳

最佳答案

现在,这是一个有趣的问题!

显而易见的(简单)解决方案是使模板疯狂。如果每个功能仅需要一个接口(interface)(而不是一个确定的类型),则可以减轻您的大部分顾虑。但是,当然这在依赖方面有其自身的惩罚,您在第4点中拒绝了该解决方案。

动态转换?

struct Feature { virtual ~Feature() {} };

class F: public Feature {};
class G: public Feature {};

现在,这样声明一个实现FG的类
class Impl: public F, public G {};

像这样需要F和G的方法
void method(Feature const& i)
{
  F const& myF = dynamic_cast<F const&>(i);
  G const& myG = dynamic_cast<G const&>(i);

  myF.f(2.0);
  myG.g(2.0);
}

诚然,可能会有一点性能损失,并且在编译时不会检查类型安全性。

模板转发

但是,这需要另一种解决方案:
namespace detail
{
  void methodImpl(F const& f, G const& g);
}

template <class T>
void method(T const& t)
{
  detail::methodImpl(t,t);
}

这将BostonLogan的方法与更好的接口(interface)结合在一起。没有用户代码应该提及detail命名空间(易于测试),如果这样,则可以确保没有人使用两个不同的对象来调用methodImpl

这似乎可以满足您的大多数需求:
  • 运行时性能等效于您的示例
  • T可能是从HZ继承的,您不在乎,只要它从FG继承,则代码将编译
  • 添加其他功能不会更改任何内容
  • ...好,您必须对接口(interface)进行模板化(method),但这是非模板方法的单线转发器,该方法可以进行实际的繁重工作。

  • 唯一令我烦恼的是methodImpl实际上有2个对同一对象的引用,这可能会给将来带来麻烦。

    摆脱多个引用

    这并非易事,但我们将包装此对象并委派工作。

    这个想法是,从继承自给定功能集的未知类型的对象中,我们可以创建一个已知类型的对象,该对象将所有操作转发到第一个对象(它在其构造函数中接受)。

    为了实现这一目标,我们还需要两件事:
  • 每个Feature应该声明一个转发器
  • 我们需要一个转发器聚合系统

  • 让我们涵盖第一点:
    class F
    {
    public:
      void f(double d);
      void f2(double d, double e) const;
    };
    
    class FForwarder
    {
    public:
      FForwarder(F& f) : m_object(f) {}
    
      void f(double d) { m_object.f(d); }
      void f2(double d, double e) const { m_object.f(d,e); }
    
    private:
      F& m_object;
    };
    

    很容易,但是很麻烦。

    让我们介绍一下聚合:
    struct nil {};
    
    template <class Head, class Tail>
    struct Aggregator: Head, Aggregator<Tail::head, Tail::tail>
    {
      typedef Head head;
      typedef Tail tail;
    
      template <class T>
      Aggregator(T& t) : Head(t), Aggregator<Tail::head, Tail::tail>(t) {}
    };
    
    template <class Head>
    struct Aggregator<Head,nil> : Head
    {
      typedef Head head;
      typedef nil tail;
    
      template <class T>
      Aggregator(T& t) : Head(t) {}
    };
    
    template <>
    struct Aggregator<nil,nil>
    {
      typedef nil head;
      typedef nil tail;
    
      template <class T>
      Aggregator(T&) {}
    };
    

    现在,继续使用。

    通过给调用者增加负担:
    int method(Aggregator<FForwarder, Aggregator<GForwarder, HForwarder> >& fgh);
    

    或通过编写模板转发器:
    namespace detail
    {
      typedef Aggregator<FForwarder, Aggregator<GForwarder, HForwarder> > methodImplArg;
      int methodImpl(methodImplArg& arg);
    }
    
    template <class T>
    int method(T& t)
    {
      detail::methodImplArg arg = detail::methodImplArg(t);
        //named temporary because it is passed by reference to non-const
    
      return detail::methodImpl(arg);
        // forward the result as well
    };
    

    这解决了methodImpl相当整齐地传递2个参数的问题,尽管它确实需要额外的工作...我想应该有一个更简单的方法,但还不能查明。

    关于c++ - 如何为任意(但在编译时定义)功能支持定义抽象基类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1723796/

    有关c++ - 如何为任意(但在编译时定义)功能支持定义抽象基类?的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    4. ruby - 如何为 emacs 安装 ruby​​-mode - 2

      我刚刚为fedora安装了emacs。我想用emacs编写ruby。为ruby​​提供代码提示、代码完成类型功能所需的工具、扩展是什么? 最佳答案 ruby-mode已经包含在Emacs23之后的版本中。不过,它也可以通过ELPA获得。您可能感兴趣的其他一些事情是集成RVM、feature-mode(Cucumber)、rspec-mode、ruby-electric、inf-ruby、rinari(用于Rails)等。这是我当前用于Ruby开发的Emacs配置:https://github.com/citizen428/emacs

    5. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

      我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

    6. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    7. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    8. ruby - 在 Ruby 中有条件地定义函数 - 2

      我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

    9. ruby - 定义方法参数的条件 - 2

      我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

    10. ruby - 如何在 Grape 中定义哈希数组? - 2

      我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

    随机推荐