如何从外包中重载方法

时间:2013-09-25 00:11:18

标签: common-lisp

鉴于szenario你有一个基础包可以通过一个类代表某些东西,另一个包想要扩展这个能力。

(defpackage :test
  (:use :cl)
  (:nicknames :test)
  (:export a-test-class
       method-a
       slot-a))

(in-package :test)

(defclass a-test-class ()
  ((slot-a
    :initform 42
    :reader slot-a)))


(defmethod method-a ((a-test-class a-test-class))
  (setf (slot-value a-test-class 'slot-a) 21)
  a-test-class)


(defpackage :exttest
  (:use :cl)
  (:export extended-a-test-class
       method-a))

(in-package :exttest)

(defclass extended-a-test-class (test:a-test-class)
  ((slot-b 
    :reader slot-b
    :initform nil)))

(defmethod method-a ((a-test-class extended-a-test-class))
  (setf (slot-value a-test-class 'slot-a) 23)
  a-test-class)

现在我得到了一个实际上并没有真正使用的函数,但是查看了a-test-classextended-a-test-class的实例列表,并且应该在所有这些实例上调用method-a,期待它们分别改变他们的类型。例如。 (slot-a (method-a a-test-class-instance)) > 21(slot-a (method-a extended-a-test-class-instance)) > 23

但是试图这样做,我遇到了正确调用method-a as:

的问题
(defparameter *test-instance* (make-instance 'test:a-test-class))
(defparameter *ext-test-instance* (make-instance 'exttest:extended-a-test-class))

(test:slot-a (test:method-a *test-instance*))
> 21
(test:slot-a (test:method-a *ext-test-instance*))
> 21

(test:slot-a (exttest:method-a *test-instance*))
(test:slot-a (exttest:method-a *ext-test-instance*))

debugger invoked on a SIMPLE-ERROR in thread
#<THREAD "main thread" RUNNING {1002B03193}>:
  There is no applicable method for the generic function
    #<STANDARD-GENERIC-FUNCTION EXTTEST:METHOD-A (1)>
  when called with arguments
    (#<TEST:A-TEST-CLASS {10041148A3}>)

这两种方式对我来说都不起作用,因为无论如何我都无法编译,或者方法的效果并不理想。如果类和方法定义在同一个包中,那么一切正常。

因此:如何在不需要处理相应包的情况下调用实例上的方法? (如果我无法这样做,我想知道我是怎么做的对Common-Lisp中OO编程的期望被误导了)

对于一个“工作”的例子,我想要输出什么,我编写了这个c ++程序。我知道CLOS与“常见”面向对象系统的工作方式不同,因为方法不属于“类”。但我希望任何面向对象的系统能够(某种程度上)能够像这样使用行为:

#include <iostream>

namespace test {
  class sub {
  public:
    virtual sub* method_a() = 0;
  };

  class a_test_class : public sub
  {
  protected:
    int value;  
  public:
   a_test_class(int val) : value(val) {
   }

   a_test_class* method_a() {
     value = 21;
     return this;
   }

   int get_value() {
      return value;
   }
  };
}

namespace exttest {

  class extended_a_test_class : public test::a_test_class {

  public:
    extended_a_test_class(int val) : a_test_class(val) {  }

   extended_a_test_class* method_a() {
     std::cout << "calling overloaded method" << std::endl;
     this->value = 23;
     return this;
   }
  };
}


int main(int argc,const char* argv[]) {
  test::a_test_class* atc = new test::a_test_class(42);
  test::a_test_class* eatc = new exttest::extended_a_test_class(42);
  std::cout << atc->method_a()->get_value() << std::endl;
  std::cout << eatc->method_a()->get_value() << std::endl;
  delete atc;
  delete eatc;
}

> ./a.out
21
calling overloaded method
23

2 个答案:

答案 0 :(得分:3)

您需要将(:import-from test #:method-a)添加到(defpackage :exttest),以便两个符号test:method-aexttest:method-a 相同

现在定义了两个单独的泛型函数exttest:method-atest:method-a,每个函数都有一个方法;前者没有为exttest:extended-a-test-class定义,而后者没有exttest:extended-a-test-class的单独方法。

答案 1 :(得分:2)

正如您可能需要编写包前缀以确保在调用函数或方法时引用正确的符号一样,您也可能需要使用包前缀当您在通用函数上定义其他方法时。例如,考虑名为“FOO”的包,以及包“FOO”中名为“FROB”的通用函数:

(defpackage #:foo 
  (:export #:frob)
  (:use "COMMON-LISP"))

(in-package #:foo)

(defgeneric frob (object))

(defmethod frob ((x string))
  (format t "frobbing string ~a" x))

;; call frob 
(frob "some string")

在另一个名为“ΒΑR”的包中,要在名为“FOO”的包中引用名为“FROB”的符号,我们必须编写foo:frob。这适用于调用通用functoin,以及在其上定义新方法。

(defpackage #:bar
  (:use "COMMON-LISP"))

(defmethod foo:frob ((x integer))
  (format t "frobbing integer ~A" x))

;; call frob 
(foo:frob "some string")
(foo:frob 45)