Vala不同类型的构造函数 [英] Vala different type of constructors

查看:78
本文介绍了Vala不同类型的构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么,这三个vala构造函数是什么?

Why, and what does the three vala constructors ?


  • 类构造

  • construct

  • 类名方法

更具体地讲,为什么在使用时从不调用第三个构造

and more specific, why the 3rd construct is never called when using it from a Gtk.Builder file?

推荐答案

简短答案:因为这就是GObject的工作原理。长答案只是更长的时间:

Short answer: because that's how GObject works. The long answer is just a smidge longer:

C没有对象或构造函数,它具有结构和函数。结构非常简单;它们包含字段,仅此而已。没有方法,构造函数,析构函数等。

C doesn't have objects or constructors, it has structs and functions. Structs are very simple; they contain fields, and that's it. No methods, constructors, destructors, etc. They look like this:

typedef struct Foo_ {
  int bar;
  char* baz;
} Foo;

要实例化一个结构,您需要分配必要的内存(在堆栈或堆上,

To "instantiate" a struct, you allocate the necessary memory (either on the stack or the heap, I'll focus on the heap for the rest of the question) and set the fields.

即使对于我们的简单结构,这很快也会让人很痛苦,所以您会发现问题的其余部分。通常会看到帮助分配和释放结构的函数。像这样的东西:

This quickly gets to be a pain, even for our simple struct, so you'll usually see functions to help out with allocating and freeing structs. Something like this:

Foo* foo_new (int bar, char* baz) {
  Foo* foo = malloc (sizeof (Foo));
  /* malloc() can fail.  Some libraries would return null, some would
     just assume it never does.  GLib-based software generally just exits
     with an error, which is what we'll do here. */
  if (NULL == foo) {
    fprintf (stderr, "%s:%d: Unable to allocate room for struct Foo\n",
             __FILE__, __LINE__);
    exit (EXIT_FAILURE);
  }
  foo->bar = bar;
  foo->baz = (NULL != baz) ? strdup (baz) : NULL;
  return foo;
}

void foo_free (Foo* foo) {
  if (NULL == foo)
    return;

  if (NULL != foo->baz)
    free (foo->baz);
  free (foo);
}

在瓦拉, * _ new 函数映射到命名构造函数。与此相关的Vala绑定可能类似于:

In Vala, the *_new functions are mapped to named constructors. The Vala binding for this might look something like:

[Compact]
public class Foo {
  public Foo ();

  public int bar;
  public string? baz;
}

这都很简单,但是当您想扩展时会发生什么 Foo 并添加一个新字段? C没有扩展结构的语言级别支持。 C程序员通过将基本结构嵌入子结构中来解决此问题:

That's all pretty simple, but what happens when you want to "extend" Foo and add a new field? C doesn't have any language-level support for "extending" a struct. C programmers get around this by embedding the base struct in the child struct:

typedef struct Qux_ {
  struct Foo parent;
  int quux;
} Qux;

在C级别,这是一个相当不错的解决方案; Qux结构的第一部分与Foo结构完全相同,因此当我们要使用 Qux 作为 Foo 我们所要做的只是强制转换:

This is a pretty decent solution at the C level; the first part of Qux struct is exactly the same as a Foo struct, so when we want to use a Qux as a Foo all we have to do is cast:

void qux_set_bar_and_qux (Qux* qux, int bar, int quux) {
  ((Foo*) qux)->bar = bar;
  qux->quux = quux;
}

不幸的是,在创建新实例时,它会严重损坏。请记住,我们的 foo_new 函数在堆上分配了 sizeof(Foo)字节的一部分(使用 malloc )— quux 字段没有空间!这意味着我们不能调用 foo_new 函数。

Unfortunately it breaks down pretty badly when creating a new instance. Remember that our foo_new function allocates a slice of sizeof(Foo) bytes on the heap (using malloc)—there is no room for the quux field! That means we can't call our foo_new function.

如果您要调用用Vala编写的库,有一种解决方法:除了 foo_new 函数之外,Vara实际上还会生成一个 foo_construct 函数。因此,假设类似

If you're calling a library written in Vala, there is a way around this: in addition to the foo_new function, Vala will actually generate a foo_construct function. So, given something like

[Compact]
public class Foo {
  public Foo (int bar, string? baz) {
    this.bar = bar;
    this.baz = baz;
  }
}

Vala实际生成的内容有点像这样:

What Vala will actually generate is something a bit like this:

void foo_construct (Foo* foo, int bar, char* baz) {
  foo->bar = bar;
  foo->baz = g_strdup (baz);
}

Foo* foo_new (int bar, char* baz) {
  Foo* foo = g_malloc (sizeof (Foo));
  foo_construct (foo, bar, baz);
  return foo;
}

现在,如果我们的 Qux 类是 Foo ,它可以调用我们的 Foo 命名构造函数:

Now, if our Qux class in Vala subclasses Foo, it can call our Foo named constructor:

[Compact]
public class Qux : Foo {
  public Qux (int quux) {
    base (0, "Hello, world");
    this.quux = quux;
  }

  public int quux;
}

因为生成的代码实际上没有调用 foo_new ,它调用 foo_construct

Because the generated code doesn't actually call foo_new, it calls foo_construct:

Qux* qux_new (int quux) {
  Qux* qux = g_malloc (sizeof (Qux));
  foo_construct ((Foo*) qux, 0, "Hello, world");
  qux->quux = quux;
}

很遗憾,用Vala编写的代码 not 很少出现此约定(使用valac分发的VAPI中 has_construct_function CCode属性的grep)。

Sadly, code not written in Vala rarely follows this convention (grep for the 'has_construct_function' CCode attribute in the VAPIs distributed with valac).

这时您可能会想,这很痛苦,但为什么不这样做呢?只需在qux_new中重新创建foo_new函数的内容即可。好吧,因为您可能无法访问 Foo 结构的内容。撰写 Foo 的人可能不希望您弄乱他们的私有字段,因此他们可以将 Foo 设为不完整的类型

At this point you might be thinking, "It's a pain, but why not just re-create the contents of the foo_new function in qux_new". Well, because you may not have access to the contents of the Foo struct. The person who wrote Foo may not want you messing with their private fields, so they can make Foo an incomplete type in the public headers, and keep the full definition to themselves.

现在,让我们开始讨论 GObject属性。我将对细节稍作介绍,但是基本上它允许您注册类型,并包含一些有关它们的信息,这些信息可以在运行时使用。

Now, let's start talking about GObject properties. I'm going to be a bit light on the details, but basically it allows you to register types, and includes a bit of information about them which is available at runtime.

在GObject中注册的类可以具有属性。从概念上讲,它们在某种程度上类似于C结构中的字段,但是该类型提供了用于加载和存储它们的回调,而不仅仅是让您的代码直接存储到地址。这也意味着它可以像设置值时发出信号一样,以及其他一些方便的东西。

Classes registered with GObject can have properties. These are conceptually somewhat similar to fields in C structs, but the type provides callbacks for loading and storing them instead of just letting your code store to an address directly. This also means it can do thinks like emit a signal when you set a value, and some other convenient stuff.

GObject中的类初始化相当复杂。我们将在稍后再讨论,但是首先让我们从要实例化GObject类的库的角度来进行研究。看起来像这样:

Class initialization in GObject is fairly complicated. We'll talk about that a bit more in a minute, but let's first look at it from the point of view of a library which wants to instantiate a GObject class. That would look something like this:

Qux* qux = g_object_new (QUX_TYPE,
                         "bar", 1729,
                         "baz", "Hello, world",
                         "quux", 1701,
                         NULL);

这可能很明显:它创建了一个 Qux 实例,并将 bar属性设置为1729,将 baz设置为 Hello,world,将 quux设置为1701。现在,回到 how 实例化该类。再次,这(略有简化),但是……

It's probably pretty obvious what this does: it creates a Qux instance, and sets the "bar" property to 1729, "baz" to "Hello, world", and "quux" to 1701. Now, back to how the class is instantiated. Again, this is (more than) a little simplified, but…

首先,足够的内存来容纳 Qux 实例(包括 Foo 父类,现在还有 GObject 类,它是所有<$ c $的祖先) c> GObject s)。

First, enough memory to hold the Qux instance (including the Foo parent class, and now also the GObject class which is the ancestor of all GObjects) is allocated.

接下来,设置 bar, baz和 qux属性的回调

Next, the callbacks to set the "bar", "baz", and "qux" properties are invoked.

接下来,调用 * _ constructor 函数。在Vala中,此映射到 construct 块。看起来像这样:

Next, the *_constructor function is called. In Vala, this is mapped to the construct block. It looks something like this:

static GObject * foo_constructor (GType type,
    guint n_construct_properties,
    GObjectConstructParam construct_properties[n_construct_properties]) {
  GObjectClass * parent_class = G_OBJECT_CLASS (foo_parent_class);
  GObject * obj = parent_class->constructor (type, n_construct_properties, construct_properties);
  Foo * self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_FOO, Foo);

  /* The code from your construct block goes here */

  return obj;
}

请注意,您无法控制此函数的参数。如您所见,每个构造函数都调用其父类的构造函数。我在构造块中的代码所在的位置添加了注释;我们将很快回到为什么它与命名构造函数分开的地方。

Note that you don't get to control the arguments to this function. As you can see, each constructor calls the constructor of its parent class. I've added a comment where the code from your construct block goes; we'll get back to why that is separate from the named constructor soon.

现在,让我们看一下命名构造函数的代码。请记住,绝大多数库没有 * _ construct 函数,因此我们可以想象一个没有的函数(对于我们的 Qux 类):

Now, let's look at the code for a named constructor. Remember, the vast majority of libraries don't have a *_construct function, so we'll imagine one which doesn't (for our Qux class):

Qux* qux_new (int bar, int quux) {
  Qux* qux = g_object_new (QUX_TYPE,
                           "bar", bar,
                           "quux", quux,
                           NULL);
  /* Code from your named constructor goes here. */
}

最后,我们了解了为什么没有调用您命名的构造函数使用 GtkBuilder 时:它不调用 qux_new ,而是调用 g_object_new 呼叫 qux_new 是一个巨大的痛苦了解您的图书馆,显然 GtkBuilder 了解您的图书馆是不可行的。

At last, we get to why your named constructor isn't called when using GtkBuilder: it doesn't call qux_new, it calls g_object_new. Calling qux_new is an enormous pain without knowledge of your library, and obviously it's not feasible for GtkBuilder to know about your library.

最后,让我们谈谈关于类构造块。他们基本上是完全不同的事情。幸运的是,解释它们的时间很短:在GObject中注册类型时调用它们,而在实例化该类型的实例时 not 调用它们。基本上,它是在您的类第一次实例化时调用的,不再调用。一个简单的例子:

Finally, let's talk about class construct blocks. They're basically an entirely different thing. Luckily, it doesn't take nearly as long to explain them: they are called when the type is registered with GObject, not when an instance of the type is instantiated. Basically, it gets called the first time your class is instantiated and never again. A quick example:

public class Foo : GLib.Object {
  class construct {
    GLib.message ("Hello, world!");
  }

  construct {
    GLib.message ("%d", this.bar);
  }

  public int bar { get; set; default = 1729; }
}

private static int main (string[] args) {
  var foo = new Foo ();
  foo = new Foo ();

  return 0;
}

会输出

Hello, world!
1729
1729

这篇关于Vala不同类型的构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆