Resolve/C++ FAQ: General Concepts

This section addresses basic concepts.

Q1: What is Resolve/C++?

Q2: Where did Resolve/C++ come from?

Q3: Why do we need yet another programming language?

Q4: What are the main benefits of Resolve/C++ compared to traditional C++?

Q5: What is Resolve Foundation?

Q6: How can I use Resolve Foundation?

Q7: Where can I find the implementation of the Foundation components?

Q8: What keywords are used in Resolve/C++?

Q9: What macros are used in Resolve/C++?

Q10: Why was it necessary to have special primitive types instead of using the built-in ones?

Q11: What is a software component catalog?

Q12: How is a software component catalog organized?

Q13: What do the statements starting with '#' do?

Q14: How can I compile a program from the command line?

Q15: What are operation parameter modes and what do they mean?

Q16: Does Resolve/C++ support numerical computations?

Q17: What is an accessor? How can/should it be used?

Q18: What debugging support is available?

Q19: Is Resolve/C++ case sensitive?

Q20: What is the index of the first Record field, Array element, Sequence element, etc.?

Q21: How can I avoid having unmatched parentheses and braces?

Q22: How is input/output done?

Q23: What is a checking component? When should it be used?

Q24: What is the swap operator? Why is swapping better than copying?

Q25: Why do all concrete templates have a parameter called Rep? What are the benefits of factoring out the Representation?

Q26: How do I set up my environment to use Resolve/C++?

Q27: How can I put a lot of text into a Text object?

Q28: How can I control the output precision and format when printing a Real value?

Q29: How does Resolve/C++ support command line processing?

Q30: How does Resolve/C++ support checking file existence and permissions?

Q31: How does Resolve/C++ support interprocess communication?

Q32: What does a Resolve/C++ main program look like?

Q33: Why does Resolve/C++ use such long component and operation names?


Q1: What is Resolve/C++?

A1: Resolve/C++ is a framework supporting highly-structured component-based software engineering. It is not really a language, but rather a discipline -- providing strict rules and guidelines -- to help a software engineer build safe, efficient, and reusable code in C++. The reason it is not "a language" is that we use an ordinary C++ compiler; there is no "Resolve/C++ compiler".


Q2: Where did Resolve/C++ come from?

A2: Resolve/C++ was (and is still being) developed by the Reusable Software Research Group (RSRG) in the Department of Computer Science and Engineering at The Ohio State University.


Q3: Why do we need yet another programming language?

A3: Resolve/C++ is not a new programming language. Rather, it is a disciplined way of programming in C++. The only preprocessor used during the compilation of Resolve/C++ code is the native C++ preprocessor. Furthermore, it is used only to introduce several keywords and macros into the language, that improve program understanding. In other words, a Resolve/C++ program is a C++ program.


Q4: What are the main benefits of Resolve/C++ compared to traditional C++?

A4:


Q5: What is Resolve Foundation?

A5: Foundation is a framework supporting programming in Resolve/C++. It provides:

Examples of these features are available here.

Q6: How can I use Resolve Foundation?

A6: As the client (Resolve/C++ programmer), you only have to

#include "RESOLVE_Foundation.h"
at the top of every compilable unit (i.e., a .cpp file), to be able to write Resolve/C++ code in that module.


Q7: Where can I find the implementation of the Foundation components?

A7: The header file RESOLVE_Foundation.h is the main header file of the Resolve Foundation, and includes all the other components. The components of the Foundation live in their respective subdirectories inside the subdirectory RESOLVE_Foundation.

Please note that as support for Resolve/C++, the Resolve Foundation is written using a different discipline, plain C++. As such, it is not a model for programming in Resolve/C++; it is a different thing. This situation is acceptable and unavoidable. For example, the machine language programs that Resolve/C++ programs ultimately compile and link to look nothing like Resolve/C++ programs and follow none of the Resolve/C++ rules. One can and should regard the Resolve Foundation as a system that, on its outside, supports the Resolve/C++ discipline of programming; its inside implementation is something completely different.


Q8: What keywords are used in Resolve/C++?

A8: The complete list of Resolve/C++ keywords can be found in the file

rcpp/RESOLVE_Foundation/Miscellaneous/Keywords.h

Q9: What macros are used in Resolve/C++?

A9: The complete list of Resolve/C++ macros can be found in the files

rcpp/RESOLVE_Foundation/Miscellaneous/Debugging_Macros.h

rcpp/RESOLVE_Foundation/Miscellaneous/Macros.h
The former contains the macros used for debugging, and the latter contains all other macros.


Q10: Why was it necessary to have special primitive types instead of using the built-in ones?

A10: The built-in primitive types (e.g., int, double, etc.) do not have certain characteristics required by the Resolve/C++ discipline of all components. The primitive types provided by the RESOLVE_Foundation have the following properties (which distinguish them from the built-in primitive types):


Q11: What is a software component catalog?

A11: A software component catalog is a "library" of software components. It is used to group related components together, and to simplify their maintenance and accessibility.


Q12: How is a software component catalog organized?

A12: Regular components, i.e., the ones not belonging to the Foundation, are organized into shared catalogs. There is one shared catalog for the entire CSE 221/222/321 series, RESOLVE_Catalog, and it is best accessed using the HTML on-line RESOLVE_Catalog browser. You can also have your own personal catalog, and typically there is a separate catalog for each lab assignment in the CSE 221/222/321 series.

In the file system, every shared catalog is organized as a separate subdirectory under the rcpp subdirectory. For example, RESOLVE_Catalog lives in the directory rcpp/RESOLVE_Catalog. Every catalog (shared or not) has four main subdirectories, called AT, AI, CT, and CI (that stand for Abstract Templates, Abstract Instances, Concrete Templates, and Concrete Instances, respectively). Each of these subdirectories contains the parts of that type of each of the components in the catalog. For example, all Concrete Templates that are a part of Stack component family in the RESOLVE_Catalog are located in the subdirectory rcpp/RESOLVE_Catalog/CT/Stack/


Q13: What do the statements starting with '#' do?

A13: Before the compiler ever sees the file containing your program, another tool, called the preprocessor, works on it a bit. In fact, the preprocessor creates a whole new source file, based on your source file, but with some work done to it. What it does to the file depends on what you tell it to do. Why, you say, I did not tell it to do anything... Ah, but you did. Every time you write a statement starting with '#', preprocessor treats it as a request to do something. The following are some of the valid ways to talk to the preprocessor.


Q14: How can I compile a program from the command line?

A14: This is quite simple:

  1. Go to the directory where your main program file is located.
  2. Run rcpp-make.
Note that the messages you will get are exactly the same as the ones in your emacs window. Also, make sure you save your program in the editor before you run rcpp-make.


Q15: What are operation parameter modes and what do they mean?

A15Operation parameter modes are a part of the specification of an operation.

There are four parameter modes:


Q16: Does Resolve/C++ support numerical computations?

A16: Yes, although some constant-overhead efficiency penalty will be incurred compared to normal C or C++ programs. The two parts of the RESOLVE_Foundation most relevant to numerical computations are Conversion_Operations and Math_Operations. The To_Text conversion operation provides the means for output of Real numbers with variable precision as well as in scientific notation. The latter provides standard mathematical operations similar to the ones in the standard C library.


Q17: What is an accessor? How can/should it be used?

A17: Certain types, e.g. List, Queue, etc., as well as Record, have a special member operation that in Resolve/C++ lingo is called the "accessor operator". This operator is used to gain "direct access" to an Item stored inside an object which is a structured collection.

Some of the types have an accessor that can be used to access any of the Items inside it, e.g. Partial_Map, Sequence, and Array. Others, e.g., List, Stack, and Queue, provide access only to one special Item, e.g., the one at the front of the Queue.

Note that since the accessor is used to access an Item inside the data structure, it is presumed that that Item is already there. In other words, while it is possible to modify the Item being accessed through the accessor, it is not possible to add or remove it. In other words, accessing an Item inside Sequence is similar to renting a pool table in a bar -- you can get in and use it, but you are not walking out the door with it!

To summarize, one should use the other operations of a data structure to Add/Remove Items to/from it, and one should use the accessor only to access the Items already inside.


Q18: What debugging support is available?

A18: First, it is recommended that the checking version of all components be used during testing and debugging, so that if a precondition violation occurs, an assertion violation message is printed, as opposed to a program crash. Second, a set of debugging macros is provided in RESOLVE_Foundation/Miscellaneous/Debugging_Macros.h along with their detailed descriptions. Briefly, these macros include:

trace : this macro results in a file/line# message when the trace line is reached. This is mostly useful for determining how far the program reaches before things go haywire.

debug(msg): this macro prints message msg, where msg is something that that you would write to a Character_OStream object using the "put to" operator, e.g.,

debug ("x = " << x << ", y =" << y);
breakpoint(msg): works like debug, but also breaks the execution and prompts the user whether or not to continue.


Q19: Is Resolve/C++ case sensitive?

A19: C++ is case sensitive. That means that, e.g., Count () and count () are completely different operations.


Q20: What is the index of the first Record field, Array element, Sequence element, etc.?

A20: The Resolve/C++ numerical indexing convention is the same as that of normal C++: all indexes start at 0. Therefore, the index of the first field of an n-field Record is 0, and the index of the last field is n-1. Same goes for the indexes of Array and Sequence elements.


Q21: How can I avoid having unmatched parenthesis and braces?

A21: Unmatched parentheses and braces result in parse errors at compile time, and can be very difficult to find. The best solution is to avoid these problems: to discipline oneself to always type in the closing parenthesis/brace for every opening one immediately, and not after the block between them is typed in. For example, when typing in an if statement, you might proceed in the following steps:

  1. Type in the if, the parentheses and the braces after it, the else, and the braces after it.
  2. if ()
    
    {
    
    }
    
    else
    
    {
    
    }
  3. Fill in the condition inside the parentheses.
  4. if (count > 0)
    
    {
    
    }
    
    else
    
    {
    
    }
  5. Fill in the code in the if and in the else block. If there is nothing to be done in the else part, delete the else block (including the braces).

Q22: How is input/output done?

A22: There are two stream types, called Character_IStream and Character_OStream that are used for input and output respectively. All Foundation types, as well as all other types exporting Get_From and Put_To operations, can be input/output by using >> and << operators respectively. However, before any input or output is done, the stream must be opened by using either Open_Internal or Open_External operation (depending on the final destination of the data) and then, after input/output is completed, closed using Close_Internal or Close_External operation respectively.

It is sometimes hard to remember whether to use "<<" or ">>". Solution -- remember that the "arrow" is pointing in the direction of the flow of the information. That is, for input, the "arrow" points from the stream object into the object that is being input into, e.g.

input_stream >> object_receiving_input;
while for output, the arrow points from the object being output to the output stream, e.g.
output_stream << object_being_output;

Incidentally, this helpful tip also applies for the input and output redirection operators used in a Unix shell command. Students often confuse the input and output redirection operators in the Unix shell with the input and output operators for streams in C++ because the "arrows" seem to point in "opposite" directions (left vs. right) in the two languages! In fact, they point in the "same" direction in both languages, i.e., in the direction of information flow. Thinking about it this way should eliminate the confusion.


Q23: What is a checking component? When should it be used?

A23: Many components have operations with preconditions. That means that before calling such an operation, the programmer must make sure that its preconditions are satisfied. If they are not, the operation is allowed to do anything it pleases, including crashing the program in most unpleasant ways.

While it may seem like a good idea to force every operation to check its preconditions, doing so would result in very severe and unnecessary efficiency penalties. The reason it is not always necessary to check the preconditions is that in the debugged or verified program one can be certain that they are satisfied. However, during the testing and debugging of a program, it is very helpful to know that if the program is calling an operation when its preconditions are not satisfied, a helpful message to that extent will be output and the program will stop immediately.

To facilitate this, a special "checking" implementation of every component should be constructed. This implementation serves as a wrapper around any non-checking implementation of this component, which is accomplished with the use of templates. The checking wrapper provides a new implementation for every operation with preconditions, and simply re-exports all the operations without preconditions.

The checking implementation of an operation with preconditions checks those preconditions, and calls through to the non-checking implementation of this operation only if the preconditions are satisfied. If they are not, it stops the program and produces an explanatory message.

Thus, during testing and debugging, checking versions of all components are used. However, the final version of the program is compiled with non-checking versions of all components, since this preserves correctness, while improving efficiency.


Q24: What is the swap operator? Why is swapping better than copying?

A24: One of the most important ideas of Resolve/C++ is the swapping paradigm. Briefly, the idea here is that in the real world, physical objects are not easily created and destroyed. Thus, when we manipulate objects in the real world, such as when we put a coin in a box, we are not creating copies (and destroying them), but rather changing the locations of the objects themselves. However, in traditional programming, the predominant way of manipulating objects is by copying.

It turns out that copying is quite expensive in the programming world as well. To overcome this, traditional programmers introduce heavy use of pointers, which are cheap to copy, but very difficult to debug. Enter the swapping paradigm.

The idea of swapping objects instead of copying them combines the best of both worlds -- code written using the swapping paradigm is both efficient and easy to reason about. Not only that, it also supports modular reasoning, that is, the ability to reason about what a component does without having to worry about its implementation.

Every Resolve/C++ component T is required to export the swap operator, with the syntax:

void T::operator &= (T& rhs);
A typical swap invocation looks like this:
q1 &= q2;
Note that all components that use the Representation component as their representation get the swap operator for free -- they do not need to (and should not) mention it in their interface, or implement it. (The same is true for the Clear operation.)


Q25: Why do all concrete templates have a parameter called Rep? What are the benefits of factoring out Representation?

A25: All Resolve/C++ concrete templates (with the rare exception of the ones implemented in native C++) have a template parameter called Rep. The actual parameter is required to be an instance of Representation component and is used as the representation of the concrete template.

Compared to the traditional C++ approach of using private data fields for representation, this schema has several important benefits. First, the memory management strategy for the representation fields is factored out and encapsulated in the Representation component. This significantly improves performance (based on a technique called "lazy initialization") without jeopardizing correctness or simplicity for the implementer of a new data type. Second, the swap operator and Clear operation are inherited and re-exported by the concrete template. Finally, this schema makes the representation fields access syntax uniform with the access syntax of record fields.


Q26: How do I set up my environment to use Resolve/C++?

A26: Follow this link for instructions.


Q27: How can I put a lot of text into a Text object?

A27: The easiest way to insert a lot of text into one text object in a formatted fashion is to use a Character_OStream object as an internal "buffer" rather than as a method of communicating with the external world. The following code inserts several lines of text (comprising an address) into one Text object called "address":

object Text address;
object Character_OStream temp;
temp.Open_Internal ();
temp << "Mr. Doyle Brunson\n" << "2354 Main Street\n";
temp << "Anywhere, Kansas 54321";
temp.Close_Internal (address);

Even better, this technique also works when some or all of the text you want to put into a Text object in a formatted way comes from some other object(s), e.g.:

object Text address;
object Character_OStream temp;
temp.Open_Internal ();
temp << name << "\n" << "2354 Main Street\n";
temp << "Anywhere, Kansas 54321";
temp.Close_Internal (address);

Q28: How can I control the output precision and format when printing a Real value?

A28: All output formatting when you use the Put_To operator (<<) for built-in types is actually done by a function called To_Text. There is one version of To_Text for each argument type Boolean, Character, Integer, and Real. So, for example, the expression To_Text(true) has the Text value "1" (perhaps unexpectedly, but this is the current state of affairs (To_Text(false) is "0")); the expression To_Text('A') has the Text value "A"; the expression To_Text(17) has the Text value "17"; and the expression To_Text(3.141592) has the Text value "3.141592". If you output To_Text of an object of a built-in type, it is just the same as outputting the object directly. So both the Put_To statements below output the same thing: "125".

  object Character_OStream outs;
  object Integer i = 125;
  object Text i_as_text = To_Text (i);
  outs << i;
  outs << i_as_text;
To_Text for a Real argument, however, gives you two formatting options not available for the other built-in types. The default behavior of the function To_Text for a Real argument is to give you 6 digits after the decimal point and ordinary (i.e., not "scientific") notation. However: So if you want to output Real object r with 3 digits to the right of the decimal point, in place of:
  out << r;
you may write:
  object Text i_nicely_formatted = To_Text (r, 3);
  out << i_nicely_formatted;
If the first one would give you:
  2.305129
the second gives you:
  2.305
Similarly, you may add the third argument "true" to To_Text of a Real, to ask for scientific notation; so writing:
  object Text i_nicely_formatted_scientific = To_Text (r, 3, true);
  out << i_nicely_formatted_scientific;
gives you:
  2.305E+00

Q29: How does Resolve/C++ support command line processing?

A29: RESOLVE_Foundation includes a Command_Line_Handler component that enables you to inspect and process arguments from the command line. In order to use the component in an application program, the procedure "main" must be supplied with a special keyword parameter called end_user_command_line. To process the command line, declare an object of type Command_Line_Handler and then initialize it by calling Get_Command_Line (command_line_arguments). This procedure uses the special keyword parameter command_line_arguments to convert the command line into (what amounts to) a Sequence of Text. The function Number_Of_Arguments() can be used to find out the length of the Sequence, which is indexed from 0 to Number_Of_Arguments() - 1. Note that Number_Of_Arguments() counts the name of the executable as an argument, so its value will always be at least 1. You can process the arguments with any of the Text operations by using the accessor operator [i], whose value is the ith argument from the original command line.

An example of the use of the command-line handling feature is provided in rcpp/examples/command_lines.


Q30: How does Resolve/C++ support checking file existence and permissions?

A30: RESOLVE_Foundation includes a File_Operations component that lets you check whether a file exists, and whether it is readable, writeable, and/or executable.

An example of the use of the checking file existence and permissions feature is provided in rcpp/examples/file_operations.


Q31: How does Resolve/C++ support interprocess communication?

A31: RESOLVE_Foundation's Character_IStream and Character_OStream components let you do interprocess communication in a "clean" way, without getting into the ugly details of using Unix sockets (upon which the following functionality is built).

If you want your program to be able to get information directly from another program, it first has to "advertise" where it is listening for input. Then you open a Character_IStream object using Open_External with no parameters. This indicates your program's willingness to accept input on that stream from any other program -- i.e., not from a file or from the keyboard as in the usual case. If the Open_External call results in the Character_IStream not being open after the call, then no other program was trying to send information, so your program can't read it. On the other hand, if the Open_External call results in the Character_IStream being open after the call, then some other program is trying to send your program information, and your program can read it (until At_EOS is true) from that Character_IStream object.

Similarly, if you want your program to send information to another program, it first needs to know where the other program is listening for input (the host name and port number it "advertised"). Then you open a Character_OStream object using Open_External with two parameters: the host name and the port number you want to send information to. If the Open_External call results in the Character_OStream not being open after the call, then the other program wasn't really willing to read information on that host and port, so your program can't send it. On the other hand, if the Open_External call results in the Character_OStream being open after the call, then the other program is willing to read your program's information, and your program can send it on that Character_OStream object.

There are a few important features regarding blocking, etc., that also become important if you want to build a robust application using streams for interprocess communication. Two detailed and commented examples that use Resolve/C++ interprocess communication are available in the rcpp/examples/ipc_streams directory:


Q32: What does a Resolve/C++ main program look like?

A32: If you're starting from scratch and want as little as possible, just copy the lines below, paste them into a text editor, and go to work.  This is the "blank page" when you're writing a Resolve/C++ main program:

#include "RESOLVE_Foundation.h"

program_body main ()
{
}
Or, if you want the standard comments, too (highly recommended), start with these lines:
//  /*--------------------------------------------------------*\
//  |   Main Program: 
//  |*--------------------------------------------------------*|
//  |   Date:         
//  |   Author:       
//  |   
//  |   Brief User's Manual:
//  |   
//  \*--------------------------------------------------------*/

///-------------------------------------------------------------
/// Global Context ---------------------------------------------
///-------------------------------------------------------------

#include "RESOLVE_Foundation.h"

///-------------------------------------------------------------
/// Interface --------------------------------------------------
///-------------------------------------------------------------

program_body main ()
{
}


Q33: Why does Resolve/C++ use such long component and operation names?

A33: It is the practice of designers in Resolve/C++ is to use long names such as "Concatenate" rather than shorter abbreviations such as "Conc". That's a mild example. Consider "Get_First_Edge_On_A_Least_Cost_Path"! We share here two things in response to this complaint: (1) a reason for this design choice, and (2) a discussion of how to use tools to ease the burden of writing long names.

(1) Resolve/C++ designers tend not to use abbreviations for the same reason that book publishers don't. Like books, software is read much more often than it is written. Imagine a book publisher telling its readers that, to reduce the readers' cost of buying the book, the publisher uses many abbreviations throughout the book, thus substantially reducing the book's size. For the convenience of the reader, a reference card showing the expansions of the abbreviations is provided. I'm guessing that this practice hasn't caught on with publishers because it would place an unacceptable burden on the readers. Readers would rather pay a little extra for a book that is largely without abbreviations, excepting those few in common usage, such as "Ms.", "Mr.", and "Dr.".

Is software really read much more often than it is written? Any successful piece of software must be modified to meet changing world conditions. If it is not successful, it doesn't need to be modified, just abandoned! To perform these modifications, people must read and understand the software. The initial version of the software may be written just once, but, if successful, it will need to be read and modified many times.

(2) Good programming environments must make it easy for the programmer to enter (or "type") these long names. Such environments are often called "Integrated Development Environments" (IDEs). A software professional should learn to make effective use of her current environment. While emacs may not be an IDE (although it has some of the features of an IDE), it is capable of drastically shortening our typing time and making it less likely to make typing mistakes. What follows is a discussion of how to get emacs to help us.

If we want emacs to help us with typing the long names that are in the RESOLVE_Catalog, we must first configure our accounts so that emacs can have easy access to the RESOLVE_Catalog. We do this by making a "soft link" in the Unix file system from our home directory to the RESOLVE_Catalog. In a Unix terminal window, in your home directory, issue the following command, including the period (".") following the space character at the end of the command:

ln -s /usr/class/sce/rcpp/RESOLVE_Catalog .

In the following, I'm going to assume that I'm going to work on a closed lab assignment, say, #4. I assume that I've already copied the closed-lab04 catalog to my home directory.

In emacs, the middle mouse button, which it calls Button2, is very handy. (The left mouse button is Button1, the right mouse button is Button3.) Also, emacs uses the character sequence "C-" to describe holding down the Ctrl ("control") key. It uses the character sequence "M-" to describe holding down the meta key. On keyboards around here, the meta key is labeled "Alt". So, in the following instructions, I will use the character sequence "Alt-" to describe holding down the Alt ("alternate") key.

One edits a file in an emacs "buffer". Emacs can have many currently active buffers which the user can switch among.

What we're aiming at is using Alt-/ to automatically complete a long word when we've just typed only the first few characters. Emacs will look in the current buffer, then in other buffers, for a word that starts with the same few characters as those that were just typed. In the closed lab I'm working on, I want words like "Number_Of_Vertices" (from Least_Cost_Path_Machine) and "Look_Up_Name" (from Id_Name_Table) to be automatically completed upon my request. So, first I want these words to already be contained among emacs's buffers. Emacs's speedbar can help me load these buffers quickly.

In emacs, I hold down Alt and strike x (Alt-x) to run `execute-extended-command', a way to run any emacs command. Then, I type "spee" followed by the <Tab> key to complete the command to "speedbar". Then I tap the <Enter> key to launch the speedbar. I can drag the title bar to place the speedbar wherever I want it. I can drag a side margin of the speedbar to make it wider, too.

In the speedbar, I can use the mouse to highlight RESOLVE_Catalog, and use Button2 to open it. In the same fashion, in sequence, I open AI, Id_Name_Table, and Kernel.h.

Back up at the top of the speedbar, I can use Button2 on AI to get back there. Then, in sequence, I open, Least_Cost_Path_Machine and Kernel.h.

Now I'm ready to use the speedbar to open Reachable.cpp from my closed-lab04 catalog. At the top of the speedbar, I can highlight and use Button2 on the tilde ("~") to get to my home directory. There I can use Button2 on closed-lab04 then Reachable.cpp. In the speedbar, there is an icon to the left of Reachable.cpp. I use Button2 on that now. That gives me a list in the speedbar of the operation bodies! Button2 on Display_Reachable_Vertices_And_Costs brings me to where I need to enter the code from my warm-up assignment.

Now for the fun! While editing this procedure body, I want to get "Number_Of_Vertices" into the buffer. I type "m.Nu" immediately followed by Alt-/. I see "Number_Of_Digits". That's not what I want! Never fear. Another Alt-/ gives me just what I want! (If I had only typed "m.N" followed by Alt-/, I would have seen "Number_Of_Digits", "NAME", "NUMBER_OF_DIGITS", and "Number_Of_Vertices" with successive uses of Alt-/.) We get even greater benefit when we want to insert "There_Is_A_Connecting_Path" or "Cost_Of_A_Least_Cost_Path".

For the curious, Alt-/ runs `dabbrev-expand'. "dabbrev" stands for "dynamic abbreviation".

Emacs's Help menu is hiding away over on the right-hand side of the menu bar.


Bruce W. Weide <weide.1@osu.edu>

Last modified: Mon Aug 15 14:29:39 EDT 2011