We would very much like to enhance the debugging capabilities of Amulet. If you think of a facility that would be useful, please let us know. An article about the debugging facilities in Amulet is available from the Amulet web site.
debugger.h
file, because it is not included by default when you include amulet.h
. The most portable way to include the debugger header file is:
#include <amulet.h>
#include DEBUGGER__H
If you press the F2 key over an object, the Inspector will do the same search for an object, but will only print out the position in the window and the object at that position. This is also very useful for finding out the coordinates of a point in the window.
// inspect the specific object
void Am_Inspect(Am_Object object);
// The next one takes the name of the object. This is useful from an interpreter.
void Am_Inspect(const char * name);
Most of the time when Amulet gets an Am_Error, you are asked if you want to go into the Inspector before going into the debugger (if the library was compiled with DEBUG on). You can type ``y'' or ``n''. If you type ``n'', then you will break right into the C++ debugger so you can look at the stack, etc. If you type ``y'', the system will try to invoke the inspector viewing an appropriate object, and sometimes selects the slot of that object where the problem is. The question is displayed to the console window after the message from Am_Error. For example:** Set called on slot which is not there. Use Add instead Object =
<Am_Window_1598>, Slot = <MAIN_INSPECTOR_WINDOW[30030]>.
** Do you want to enter the Inspector viewing object <Am_Window_1598>? (type
\Qy' or \Qn'): y
Answer was \Qy'. Starting Inspector...
~~~ Inspecting object <Am_Window_1598>
Since the Amulet system has crashed, it is not always successful at invoking the Inspector, usually because some internal Amulet state has been so messed up by the error that it cannot recover sufficiently to invoke the Inspector. (If you can reproduce one of these situations, please send it to us so we can try to fix it!).extern void Am_Inspect_And_Main_Loop(const Am_Object& object,
Am_Slot_Key slot = Am_NO_SLOT);
You can also try invoking this function explicitly from your C++ debugger, if necessary.10.3.1.1 Changing the Keys
If you want to use F1, F2, and F3 for your own functions, you can always eliminate all of debugging facilities, including the inspector, by building a non-debug version of the library (see the Overview Chapter).extern void Am_Set_Inspector_Keys(Am_Input_Char show_key,
Am_Input_Char show_position_key,
Am_Input_Char ask_key);
which is exported from debugger.h
. This function takes an Am_Input_Char, but a string will also work. Passing in the null character to any of these, by using Am_Input_Char()
, will mean that that function is not available. The first parameter controls the normal popping up of the Inspector (normally bound to F1), the second parameter is for just printing the object under the mouse (normally F2), and the third is to prompt from the keyboard (normally F3). The test program testinter has a test of rebinding the Inspector's keys using this function.10.3.2 Overview of Inspector User Interface and Menus
If you single click with the left mouse button over a slot value, a cursor will appear and you can edit the slot's value. The editing keys are the same as for all other text interactors (Section 5.3.5.5.1 of the Interactors chapter). Currently, the inspector will not let you change the type of the value in the slot. Therefore, it uses the current type of the value to decide how to parse the input value. You can edit primitive values, like integers, floats and strings. Depending on whether your compiler supports bools as a primitive type, they will either print out as true and false or 1 and 0. Floating point values print out just like integers if there is no fraction part. You can use the slot properties pop-up window to find out the exact slot type.
For slots which contain named values, like styles (
Am_Red
, Am_Line_8
), objects (Am_Rectangle_123
), constraint names (windows_is_color
) and method names (rectangle_draw
), you can type in a new name. Amulet remembers the names of all built-in or user-defined objects, methods and formulas. If you create your own styles or wrappers, you can arrange for them to have names registered in the database using the ``registry'' mechanism defined in registry.h. For any wrapper object, you can register its name using the Am_Register_Name
procedure, such as: Am_Register_Name (my_color_object, "my_color");
Then, the user will be able to type in my_color
as the value of a slot.Am_Value_Lists
, and you cannot set the items of an Am_Value_List
.10.4 Accessing Debugging Functions Procedurally
Sometimes it might be useful to access the debugging functions from a program, instead of interactively from the Inspector. For example, you program might be crashing even before it fully starts up, so you cannot access the inspector. If you program gets past the Am_Initialize(), then you can still trace slot setting and print the values of the slots of objects. Also, some of these procedures might be executed from a debugger such as gdb that supports calling functions.// inspect the specific object
void Am_Inspect(Am_Object object);
// The next one takes the name of the object. This is useful from an interpreter.
void Am_Inspect(const char * name);
You can cause an object to be ``flashed'' so you can see where it is on the screen. If it is not visible, then this functions writes the reason to the specified stream:void Am_Flash (Am_Object o, ostream &flashout = cout);
The tracing functions provide significantly more features than are available interactively from the inspector. The tracing and breaking function takes an optional object, an optional slot, and an optional value. Whatever ones of these are supplied will control whether to trace or break. Thus, if only the object is supplied, then the trace or break will happen whenever any of the slots of that object are set. If only a value is supplied, then a trace or break will happen whenever any slot of any object is set to that value. If all three parameters are supplied, then a trace or break will happen only when that slot of that object is set to that value.void Am_Notify_On_Slot_Set (Am_Object object = Am_No_Object,
Am_Slot_Key key = 0,
Am_Value value = Am_No_Value);
void Am_Break_On_Slot_Set (Am_Object object = Am_No_Object,
Am_Slot_Key key = 0,
Am_Value value = Am_No_Value);void Am_Clear_Slot_Notify (Am_Object object = Am_No_Object,
Am_Slot_Key key = 0,
Am_Value value = Am_No_Value);
10.5 Functions useful in the C++ Debugger
We have provided a set of functions and methods to be invoked while in the C++ debugger to find out information about values. Most of these work for any type of Amulet object (objects, wrappers, methods, etc.). Some of these might also be useful for interactive editors and other tools that need to describe values to users.
const char * To_String() const
- tries to get a string name for the object. Does not allocate memory, so may return NULL if the object does not have a string name handy.
void Print(ostream& out) const
- prints the value of the object to the specified stream.
void Println() const
- prints the value to cout followed by a newline. This is useful in the debugger since it works for objects that might not have strings available, so To_String doesn't work. (For some reason gdb doesn't seem to like this function.)
const char* Am_Get_Slot_Name (Am_Slot_Key key)
- given a slot key number, returns the name
void Text_Inspect()
- prints out the slots and their values of an object.
const char* Am_To_String(const Am_Value &value)
- this will allocate memory if the value returns NULL for To_String()
Am_START_WHERE_TEST
of the Interactor does not return an object when you expect it to. Debugging this usually involves setting breakpoints in your start where function.
Am_START_WHERE_TEST
of Interactors tries to be smart about which object to affect, based on the type of the object the Interactor is attached to. You can specifically tell the Interactor which to affect by setting the Am_START_WHERE_TEST
slot to one of the built-in functions (such as Am_Inter_In
or Am_Inter_In_Part
) or to a custom function.
Am_VALUE
slot of the widget itself. Although the Am_VALUE
slot of the command object usually provides the same value, it does not affect the widget to set this slot in the command object. For button-type widgets, the value should either be NULL, or the label or ID of the particular item to become the value (based on the value of the Am_LABEL
slot or Am_ID
slot of the command object for that item).
Am_FINAL_FEEDBACK_WANTED
slot to true.
v.Valid()
tests, or instead of getting a slot value directly into a variable, use an intermediate Am_Value
. For example:
i = 3 / (int)obj.Get(SLOT); //crashes because returns 0 when not initialized
Am_Value v = obj.Get(SLOT);
if (v.Valid()) {
i = 3 / (int)v;
obj.Set(SLOT,my_formula.Multi_Constraint());