Home Notices Documents Classes Download Others Rice
Documents  >  Development  >  Dynamic embedded classes  >  Non-interface part
Implementation of non-interface part

Purpose of this page

This page details the first part of the five part that divided the Rtemplate.cs.


This first part is different from the other four parts.

The other four parts define class members such as fitters and methods and their behavior. In other words, the goal is to implement a class interface.

On the other hand, the first part defines the internal mechanism of the class hidden from the user. that is, the goal is to implement functions used by the interface.



As shown in the figure, the first part will be further divided into five parts, and each will be described in a individual section.

①Class name etc

This part is the part required as a C # source file. It is specifying Using directives, namespace, class name and access modifier.


Here, the point is the class name.

The C# class name is "Rtemplate", and the name on Rice will be "template".

There is no problem if the C# class name (Rtemplate) and Rice's class name (template) are completely different, but there is no need to bring in the cause of the confusion.

Rice recommends that all embedded classes use the class name that made from prefix "R" and Rice's class name.


It is important that Rtemplate is a derived class of Rtype. An embedded class must be derived from the Rtype class.


②Class name(for Rice)

Look at the first figure. The class name on the Rice is defined at the 14th line .

public const string TYPENAME = "template";

The "template" has been defined as a string constant. This TYPENAME constant does not necessarily need to be defined. For example, you may type "template" every time you need a Rice class name on the Rtemplate class.

However, you should define the constant If the same value is repeatedly referenced as a coding technique.


A constructor without arguments (default constructor) has been defined at the 16th line. This prevents the automatic generation of default constructor by the C# compiler.

This default constructor assigns class name ("template") to _typename. This _typename is a protected field declared in the Rtype definition.

The TypeName getter that is explained later refers to this _typename and returns the class name. So, _typename must be set correctly for each instance when an instance of a embedded class is created.

Needless to say, the auto-generated default constructor does not do this. Instances may be generated in unintended situations. You should define a default constructor so that the correct instance is generated even in such a case.


Although Rtemplate only defines constructor without arguments, the actual embedded class is going to have constructors with several arguments. Remember to set _typename in such constructors as well.

③Instance

The static InstanceGenerator() method is defined at the 18th line. This method has responsibility for creating class instance. This method will be registered in Rice and called when the template class is needed.

For example, suppose we have the following Rice source code.


template temp;


It is a declaration statement of template class. At this time, an instance of the template class referenced by temp is generated by calling the InstanceGenerator() method.

Registering class in Rice is done by registering a class name and a method that does not have an argument and returns Rtype .For example, registration of the template class to Rice will be as follows.



Any method can be used in class registration if it returns Rtype without any arguments.

For example, the name does not necessarily need to be "InstanceGenerator", and you may perform advanced initialization to the Rtype instance to be returned.

However, it should be sufficient to return an instance returned by the default constructor in most cases.

④_replica()

The _replica() method is defined at the 19th line. This is an abstract method declared in the definition of Rtype, and must be implemented in a derived class of Rtype. That is, it must be implemented in an embedded class.

Describes what the _replica() method is for.


Suppose we have the following Rice user-defined class.

1:class userdef
2:template _temp;
3:// Following is the member definition other than _temp field.
4:...
5:endclass

The point here is that the field of template class is declared.


It assumes a variable of this userdef class is declared somewhere in a Rice script.


userdef ud;


What do you think will happen at this time?

In the case of the template class, the InstanceGenerator() method will be called for generating an instance. This is possible because the template class is defined and registered before executing the Rice script.

On the other hand, the userdef class (Rice user-defined class) is a class will be defined after the Rice script starts executing. In other words, we do not know what kind of class it is until we start execution.

A user-defined class can not register an method like InstanceGenerator() because class body is not defined yet. But, we have to generate a user-defined class.


Rice handles this problem as follows.

Rice creates one instance as an original instance after parsing the definition of a user-defined class. Then register the class name and its instance. In other words, the embedded class registers an instantiation method, whereas the user-defined class registers an original instance.

Rice returns a copy of the original instance for a request of the user-defined class generation. This copy operation to generate a user-defined class is called replica operation in Rice.


Well, remember the definition of the userdef class. The userdef has the _temp field of the template class. Therefore, the registered original of the userdef also has _temp. Since the creation of userdef is a copy of the original, _temp must also be copied. The _replica() method will be called when this _temp will be copied.

In other words, the _replica() method is a method for copy operation that is called in the process of creating an instance of a user-defined class when the embedded class becomes a field of the user-defined class.

If special processing is required when replica operation, you can do it within the _replica() method.

However, it should be sufficient to return an instance returned by the default constructor in most cases.


Please ignore the argument of _replica() method. This is a trace of the previous implementation and should not need to be used in most case.

⑤Ftted()

The Fitted() method is defined at the 20th line. This is an abstract method declared in the definition of Rtype, and must be implemented in a derived class of Rtype. That is, it must be implemented in an embedded class.

The Fitted getter that will be detailed later refers to this Fitted() and returns the instance initialization status.

The template class has no field to hold the state of the instance, so there is no need to think of initialization status. In such a case, it is sufficient for the Fitted() method to always return true (return new Rbool (true);).


We are explained the case of meaningful class about initialization status using regex class as an example.

1:public class Rregex : Rtype {
2:private Regex _pattern = null;
3:public const string TYPENAME = "regex";
4:
5:public override Rbool Fitted(VirtualMachine vm) { return new Rbool(_pattern != null); }
6:...
7:
† Only the parts necessary for explanation are displayed.

The regex (Rregex) class is a class for performing string match determination using regular expressions. Therefore, it has the _pattern field that is C# regular expression class.

In order for this class to work properly, _pattern must be assigned an instance of Regex class initialized with a regular expression pattern.

For example

_pattern = new Regex("^Rice"); // A regular expression representing that the beginning starts with "Rice".

It is necessary to assign as above.

In fact, the regex (Rregex) class makes this assignment in the fitter. And match determination is done using _pattern field.

If the _pattern field is assigned, an instance has been initialized because can judged. Otherwise an instance has been uninitialized.

The Fitted() method is used to represent such an instance's state. An implementation of the above is as follows.

The implementation of the Fitted() method in Regex class is as follows.


public override Rbool Fitted(VirtualMachine vm) { return new Rbool(_pattern != null); }


The result of null check of _pattern is returned. This is referenced by the Fitted Getter to represent the initialization state of an instance.

If special judgement is required, you can do it within the Fitted() method. However, it should be sufficient to return a result of simple judgement like null check in most cases.


Please ignore the argument of Fitted() method. This is a trace of the previous implementation and should not need to be used in most case.

Next page

It concludes the description of the various functions used by the class interface. The described functionality here is minimal, but the embedded classes will work correctly if they are implemented correctly.

The next page describes the implementation of fitter.

Next
Previous
Copyright © CookerGX All rights reserved.