Introduction to CORBA IDL

本文介绍CORBA接口定义语言(IDL)的基本概念和语法,包括模块定义、接口定义、属性与操作声明等内容,并详细解释了如何通过IDL定义分布式应用中的对象接口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

The CORBA Interface Definition Language (IDL) is used to define interfaces to objects in your network. This chapter introduces the features of CORBA IDL and illustrates the syntax used to describe interfaces.

The first step in developing a CORBA application is to define the interfaces to the objects required in your distributed system. To define these interfaces, you use CORBA IDL.

IDL allows you to define interfaces to objects without specifying the implementation of those interfaces. To implement an IDL interface, you define a C++ class that can be accessed through that interface and then you create objects of that class within an Orbix server application.

In fact, you can implement IDL interfaces using any programming language for which an IDL mapping is available. An IDL mapping specifies how an interface defined in IDL corresponds to an implementation defined in a programming language. CORBA applications written in different programming languages are fully interoperable.

CORBA defines standard mappings from IDL to several programming languages, including C++, Java, and Smalltalk. The Orbix IDL compiler converts IDL definitions to corresponding C++ definitions, in accordance with the standard IDL to C++ mapping.

IDL Modules and Scoping

An IDL module defines a naming scope for a set of IDL definitions. Modules allow you to group interface and other IDL type definitions in logical name spaces. When writing IDL definitions, always use modules to avoid possible name clashes.

The following example illustrates the use of modules in IDL:

// IDL
module BankSimple {

    interface Bank {
    ...
    };

    interface Account {
    ...
    };
};

The interfaces Bank and Account are scoped within the module BankSimple. IDL definitions are available directly within the scope in which you define them. In other naming scopes, you must use the scoping operator (::) to access these definitions. For example, the fully scoped name of interfaces Bank and Account are BankSimple::Bank and BankSimple::Account respectively.

IDL modules can be reopened. For example, a module declaration can appear several times in a single IDL specification if each declaration contains different data types. In most IDL specifications, this feature of modules is not required.

Defining IDL Interfaces

An IDL interface describes the functions that an object supports in a distributed application. Interface definitions provide all of the information that clients need to access the object across a network.

Consider the example of an interface that describes objects which implement bank accounts in a distributed application. The IDL interface definition is as follows:

//IDL
module BankSimple {

  // Define a named type to represent money.
  typedef float CashAmount;
      // Forward declaration of interface Account.
  interface Account;   interface Bank {     ...   };   interface Account {     // The account owner and balance.     readonly attribute string name;     readonly attribute CashAmount balance;     // Operations available on the account.     void deposit (in CashAmount amount);     void withdraw (in CashAmount amount);   }; };

The definition of interface Account includes both attributes and operations. These are the main elements of any IDL interface definition.

Attributes in IDL Interface Definitions

Conceptually, attributes correspond to variables that an object implements. Attributes indicate that these variables are available in an object and that clients can read or write their values.

In general, attributes map to a pair of functions in the programming language used to implement the object. These functions allow client applications to read or write the attribute values. However, if an attribute is preceded by the keyword readonly, then clients can only read the attribute value.

For example, the Account interface defines the attributes name and balance. These attributes represent information about the account which the object implementation can set, but which client applications can only read.

Operations in IDL Interface Definitions

IDL operations define the format of functions, methods, or operations that clients use to access the functionality of an object. An IDL operation can take parameters and return a value, using any of the available IDL data types.

For example, the Account interface defines the operations deposit() and withdraw() as follows:

//IDL
module BankSimple {
  typedef float CashAmount;
  ...
  
  interface Account {
    // Operations available on the account.
    void deposit(in CashAmount amount);
    void withdraw(in CashAmount amount);
    ...
  };
};

Each operation takes a parameter and has a void return type.

Each parameter definition must specify the direction in which the parameter value is passed. The possible parameter passing modes are as follows:

inThe parameter is passed from the caller of the operation to the object.
outThe parameter is passed from the object to the caller.
inoutThe parameter is passed in both directions.

Parameter passing modes clarify operation definitions and allow an IDL compiler to map operations accurately to a target programming language.

Raising Exceptions in IDL Operations

IDL operations can raise exceptions to indicate the occurrence of an error. CORBA defines two types of exceptions:

  • System exceptions are a set of standard exceptions defined by CORBA.
  • User-defined exceptions are exceptions that you define in your IDL specification.

Implicitly, all IDL operations can raise any of the CORBA system exceptions. No reference to system exceptions appears in an IDL specification.

To specify that an operation can raise a user-defined exception, first define the exception structure and then add an IDL raises clause to the operation definition. For example, the operation withdraw() in interface Account could raise an exception to indicate that the withdrawal has failed, as follows:

// IDL
module BankExceptions {
  typedef float CashAmount;
  ...
  
  interface Account {
    exception InsufficientFunds {
      string reason;
    };
    
    void withdraw(in CashAmount amount)
      raises(InsufficientFunds);
    ...
  };
};

An IDL exception is a data structure that contains member fields. In the preceding example, the exception InsufficientFunds includes a single member of type string.

The raises clause follows the definition of operation withdraw() to indicate that this operation can raise exception InsufficientFunds. If an operation can raise more then one type of user-defined exception, include each exception identifier in the raises clause and separate the identifiers using commas.

Invocation Semantics for IDL Operations

By default, IDL operations calls are synchronous, that is a client calls an operation and blocks until the object has processed the operation call and returned a value. The IDL keyword oneway allows you to modify these invocation semantics.

If you precede an operation definition with the keyword oneway, a client that calls the operation will not block while the object processes the call. For example, you could add a oneway operation to interface Account that sends a notice to an Account object, as follows:

module BankSimple {
  ...
  
  interface Account {
    oneway void notice(in string text);
    ...
  };
};

Orbix does not guarantee that a oneway operation call will succeed; so if a oneway operation fails, a client may never know. There is only one circumstance in which Orbix indicates failure of a oneway operation. If a oneway operation call fails before Orbix transmits the call from the client address space, then Orbix raises a system exception.

A oneway operation can not have any out or inout parameters and can not return a value. In addition, a oneway operation can not have an associated raises clause.

Passing Context Information to IDL Operations

CORBA context objects allow a client to map a set of identifiers to a set of string values. When defining an IDL operation, you can specify that the operation should receive the client mapping for particular identifiers as an implicit part of the operation call. To do this, add a context clause to the operation definition.

Consider the example of an Account object, where each client maintains a set of identifiers, such as sys_time and sys_location that map to information that the operation deposit() logs for each deposit received. To ensure that this information is passed with every operation call, extend the definition of deposit() as follows:

// IDL
module BankSimple {
  typedef float CashAmount;
  ...
  
  interface Account {
    void deposit(in CashAmount amount)
      context("sys_time", "sys_location");
    ...
  };
};

A context clause includes the identifiers for which the operation expects to receive mappings.

Note that IDL contexts are rarely used in practice.

Inheritance of IDL Interfaces

IDL supports inheritance of interfaces. An IDL interface can inherit all the elements of one or more other interfaces.

For example, the following IDL definition illustrates two interfaces, called CheckingAccount and SavingsAccount, that inherit from interface Account:

// IDL
module BankSimple{
  interface Account {
    ...
  };

  interface CheckingAccount : Account {
    readonly attribute overdraftLimit;
    boolean orderChequeBook ();
  };

  interface SavingsAccount : Account {
    float calculateInterest ();
  };
};

Interfaces CheckingAccount and SavingsAccount implicitly include all elements of interface Account.

An object that implements CheckingAccount can accept invocations on any of the attributes and operations of this interface, and on any of the elements of interface Account. However, a CheckingAccount object may provide different implementations of the elements of interface Account to an object that implements Account only.

The following IDL definition shows how to define an interface that inherits both CheckingAccount and SavingsAccount:

// IDL
module BankSimple {
  interface Account {
    ...
  };

  interface CheckingAccount : Account {
    ...
  };

  interface SavingsAccount : Account {
    ...
  };

  interface PremiumAccount : 
    CheckingAccount, SavingsAccount {
    ...
  };
};

Interface PremiumAccount is an example of multiple inheritance in IDL. Figure 3.1 on page 59 illustrates the inheritance hierarchy for this interface.

If you define an interface that inherits from two interfaces which contain a constant, type, or exception definition of the same name, you must fully scope that name when using that constant, type, or exception. An interface can not inherit from two interfaces that include operations or attributes that have the same name.

Figure 3.1: Multiple Inheritance of IDL Interfaces
The Object Interface Type

IDL includes the pre-defined interface Object, which all user-defined interfaces inherit implicitly. The operations defined in this interface are described in the Orbix C++ Edition Programmer's Reference.

While interface Object is never defined explicitly in your IDL specification, the operations of this interface are available through all your interface types. In addition, you can use Object as an attribute or operation parameter type to indicate that the attribute or operation accepts any interface type, for example:

// IDL
interface ObjectLocator
{
  void getAnyObject (out Object obj);
};

Note that it is not legal IDL syntax to inherit interface Object explicitly.

Forward Declaration of IDL Interfaces

In an IDL definition, you must declare an IDL interface before you reference it. A forward declaration declares the name of an interface without defining it. This feature of IDL allows you to define interfaces that mutually reference each other.

For example, IDL interface Bank includes an operation of IDL interface type Account, to indicate that Bank stores a reference to an Account object. If the definition of interface Account follows the definition of interface Bank, you must forward declare Account as follows:

// IDL
module BankSimple {
  // Forward declaration of Account.
  interface Account;

  interface Bank {
    Account create_account (in string name);
    Account find_account (in string name);
  }; 
  // Full definition of Account.
  interface Account {
    ...
  };
};

The syntax for a forward declaration is the keyword interface followed by the interface identifier.

Overview of the IDL Data Types

In addition to IDL module, interface, and exception types, there are three general categories of data type in IDL:

  • Basic types.
  • Complex types.
  • Pseudo object types.

This section examines each category of IDL types in turn and also describes how you can define new data type names in IDL.

IDL Basic Types

The following table lists the basic types supported in IDL.

IDL TypeRange of Values
short-2 15...2 15-1 (16-bit)
unsigned short0...2 16-1 (16-bit)
long-2 31...2 31-1 (32-bit)
unsigned long0...2 32-1 (32-bit)
long long-2 63...2 63-1 (64-bit)
unsigned long long0...-2 64 (64-bit)
floatIEEE single-precision floating point numbers.
doubleIEEE double-precision floating point numbers.
charAn 8-bit value.
booleanTRUE or FALSE.
octetAn 8-bit value that is guaranteed not to undergo any conversion during transmission.
anyThe any type allows the specification of values that can express an arbitrary IDL type.

The any data type allows you to specify that an attribute value, an operation parameter, or an operation return value can contain an arbitrary type of value to be determined at runtime. Type any is described in detail in Chapter 12, "The Any Data Type" on page 239.

IDL Complex Types

This section describes the IDL data types enum, struct, union, string, sequence, array, and fixed.

Enum

An enumerated type allows you to assign identifiers to the members of a set of values, for example:

// IDL
module BankSimple {
  enum Currency {pound, dollar, yen, franc};

  interface Account {
    readonly attribute CashAmount balance;
    readonly attribute Currency balanceCurrency;
    ...
  };
};

In this example, attribute balanceCurrency in interface Account can take any one of the values pound, dollar, yen, or franc.

Struct

A struct data type allows you to package a set of named members of various types, for example:

// IDL
module BankSimple{
    struct CustomerDetails {
      string name;
      short age;
    };

    interface Bank {
      CustomerDetails getCustomerDetails
        (in string name);
      ...
    };
};

In this example, the struct CustomerDetails has two members. The operation getCustomerDetails() returns a struct of type CustomerDetails that includes values for the customer name and age.

Union

A union data type allows you to define a structure that can contain only one of several alternative members at any given time. A union saves space in memory, as the amount of storage required for a union is the amount necessary to store its largest member.

All IDL unions are discriminated. A discriminated union associates a label value with each member. The value of the label indicates which member of the union currently stores a value.

For example, consider the following IDL union definition:

// IDL
struct DateStructure {
  short Day;
  short Month;
  short Year;
};

union Date switch (short) {
  case 1: string stringFormat;
  case 2: long digitalFormat;
  default: DateStructure structFormat;
};

The union type Date is discriminated by a short value. For example, if this short value is 1, then the union member stringFormat stores a date value as an IDL string. The default label associated with the member structFormat indicates that if the short value is not 1 or 2, then the structFormat member stores a date value as an IDL struct.

Note that the type specified in parentheses after the switch keyword must be an integer, char, boolean or enum type and the value of each case label must be compatible with this type.

String

An IDL string represents a character string, where each character can take any value of the char basic type.

If the maximum length of an IDL string is specified in the string declaration, then the string is bounded. Otherwise the string is unbounded.

The following example shows how to declare bounded and unbounded strings:

// IDL
module BankSimple {
      interface Account {
        // A bounded string with maximum length 10.
        attribute string<10> sortCode;
  
        // An unbounded string.
        readonly attribute string name; 
        ...
      };
};
Sequence

In IDL, you can declare a sequence of any IDL data type. An IDL sequence is similar to a one-dimensional array of elements.

An IDL sequence does not have a fixed length. If the sequence has a fixed maximum length, then the sequence is bounded. Otherwise, the sequence is unbounded.

For example, the following code shows how to declare bounded and unbounded sequences as members of an IDL struct:

// IDL
module BankSimple {
  interface Account {
    ...
  };

  struct LimitedAccounts {
    string bankSortCode<10>;
    // Maximum length of sequence is 50.
    sequence<Account, 50> accounts;
  }; 
  struct UnlimitedAccounts {
    string bankSortCode<10>;
    // No maximum length of sequence.
    sequence<Account> accounts;
  }; 
}; 

A sequence must be named by an IDL typedef declaration before it can be used as the type of an IDL attribute or operation parameter. Refer to "Defining Data Type Names and Constants" on page 68 for details. The following code illustrates this:

// IDL
module BankSimple {
  typedef sequence<string> CustomerSeq;

  interface Account {
    void getCustomerList(out CustomerSeq names);
    ...
  }; 
}; 
Arrays

In IDL, you can declare an array of any IDL data type. IDL arrays can be multi-dimensional and always have a fixed size. For example, you can define an IDL struct with an array member as follows:

// IDL
module BankSimple {
  ...
  
  interface Account {
    ...
  }; 

  struct CustomerAccountInfo {
    string name;
    Account accounts[3];
  }; 

  interface Bank {
    getCustomerAccountInfo (in string name,
      out CustomerAccountInfo accounts);
    ...
  }; 
}; 

In this example, struct CustomerAccountInfo provides access to an array of Account objects for a bank customer, where each customer can have a maximum of three accounts.

An array must be named by an IDL typedef declaration before it can be used as the type of an IDL attribute or operation parameter. The IDL typedef declaration allows you define an alias for a data type, as described in "Defining Data Type Names and Constants" on page 68.

The following code illustrates this:

// IDL
module BankSimple {
  interface Account {
    ...
  };

  typedef Account AccountArray[100];

  interface Bank {
    readonly attribute AccountArray accounts;
    ...
  };
};

Note that an array is a less flexible data type than an IDL sequence, because an array always has a fixed length. An IDL sequence always has a variable length, although it may have an associated maximum length value.

Fixed

The fixed data type allows you to represent number in two parts: a digit and a scale. The digit represents the length of the number, and the scale is a non-negative integer that represents the position of the decimal point in the number, relative to the rightmost digit.

module BankSimple {
      typedef fixed<10,4> ExchangeRate;
      
      struct Rates {
            ExchangeRate USRate;
            ExchangeRate UKRate;
            ExchangeRate IRRate;
      };
};

In this case, the ExchangeRate type has a digit of size 10, and a scale of 4. This means that it can represent numbers up to (+/-)999999.9999.

The maximum value for the digits is 31, and scale cannot be greater than digits. The maximum value that a fixed type can hold is equal to the maximum value of a double.

Scale can also be a negative number. This means that the decimal point is moved scale digits in a rightward direction, causing trailing zeros to be added to the value of the fixed. For example, fixed <3,-4> with a numeric value of 123 actually represents the number 1230000. This provides a mechanism for storing numbers with trailing zeros in an efficient manner.


Note:   Fixed <3, -4> can also be represented as fixed <7, 0>.

Constant fixed types can also be declared in IDL. The digits and scale are automatically calculated from the constant value. For example:

module Circle {
      const fixed pi = 3.142857;
};

This yields a fixed type with a digits value of 7, and a scale value of 6.

IDL Pseudo Object Types

CORBA defines a set of pseudo object types that ORB implementations use when mapping IDL to some programming languages. These object types have interfaces defined in IDL but do not have to follow the normal IDL mapping for interfaces and are not generally available in your IDL specifications.

You can use only the following pseudo object types as attribute or operation parameter types in an IDL specification:

CORBA::NamedValue
CORBA::Principal
CORBA::TypeCode

To use any of these three types in an IDL specification, include the file orb.idl in the IDL file as follows:

// IDL
#include <orb.idl>
...

This statement indicates to the IDL compiler that types NamedValue, Principal, and TypeCode may be used. The file orb.idl should not actually exist in your system. Do not name any of your IDL files orb.idl.

Defining Data Type Names and Constants

IDL allows you to define new data type names and constants. This section describes how to use each of these features of IDL.

Data Type Names

The typedef keyword allows you define a meaningful or more simple name for an IDL type. The following IDL provides a simple example of using this keyword:

// IDL
module BankSimple {
  interface Account {
    ...
  };

  typedef Account StandardAccount;
}; 

The identifier StandardAccount can act as an alias for type Account in subsequent IDL definitions. Note that CORBA does not specify whether the identifiers Account and StandardAccount represent distinct IDL data types in this example.

Constants

IDL allows you to specify constant data values using one of several basic data types. To declare a constant, use the IDL keyword const, for example:

// IDL
module BankSimple {
  interface Bank {
    const long MaxAccounts = 10000;
    const float Factor = (10.0 - 6.5) * 3.91;
    ...
  };
};

The value of an IDL constant cannot change. You can define a constant at any level of scope in your IDL specification. 

内容概要:本文档主要展示了C语言中关于字符串处理、指针操作以及动态内存分配的相关代码示例。首先介绍了如何实现键值对(“key=value”)字符串的解析,包括去除多余空格和根据键获取对应值的功能,并提供了相应的测试用例。接着演示了从给定字符串中分离出奇偶位置字符的方法,并将结果分别存储到两个不同的缓冲区中。此外,还探讨了常量(const)修饰符在变量和指针中的应用规则,解释了不同类型指针的区别及其使用场景。最后,详细讲解了如何动态分配二维字符数组,并实现了对这类数组的排序与释放操作。 适合人群:具有C语言基础的程序员或计算机科学相关专业的学生,尤其是那些希望深入理解字符串处理、指针操作以及动态内存管理机制的学习者。 使用场景及目标:①掌握如何高效地解析键值对字符串并去除其中的空白字符;②学会编写能够正确处理奇偶索引字符的函数;③理解const修饰符的作用范围及其对程序逻辑的影响;④熟悉动态分配二维字符数组的技术,并能对其进行有效的排序和清理。 阅读建议:由于本资源涉及较多底层概念和技术细节,建议读者先复习C语言基础知识,特别是指针和内存管理部分。在学习过程中,可以尝试动手编写类似的代码片段,以便更好地理解和掌握文中所介绍的各种技巧。同时,注意观察代码注释,它们对于理解复杂逻辑非常有帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值