small Editline wrapper cleanup req for feedback

Some comments in https://reviews.llvm.org/rGfd89af6880f33ead708abe2f7d88ecb687d4e0d2 prompted me to look more into potential simplifications of our EditLine wrapper and I wanted to run this by anyone who is interested before making the changes.

Right now we set a bunch of callbacks in libedit that are captureless lambdas implicitly converted to C function pointers. The lambdas look up an instance of our Editline class and invoke member functions. The boilerplate that could be generated by templates is something like the following:

class Foo { // Imagine this is our Editline class that wraps libedit
public:
unsigned char Foo1(int ch) { // These are member functions invoked by lambdas we pass to libedit
return ‘a’;
}
unsigned char Bar(int ch) {
return ‘b’;
}
unsigned char Baz(int ch) {
return ‘c’;
}
};

typedef unsigned char (elFnPtr)(EditLine, int); // Signature of callbacks libedit takes (note Edit__L__ine is libedit, and Edit__l__ine is our wrapper)
typedef unsigned char (Foo::*FooMemPtr)(int ch); // Signature of member functions invoked

template<FooMemPtr callback, typename Fn = elFnPtr>
tuple<const char*, const char*, Fn> createEditLineCommandDescriptor(const char* command, const char* helpText) {
return make_tuple(command, helpText, [] (EditLine*, int ch) {
cout << ch;
Foo foo;
((&foo)->*callback)(‘a’);
return (unsigned char) ch;
});
}

auto editlineCommands = {
createEditLineCommandDescriptor<&Foo::Foo1>(“Command1", “Command1 help"),
createEditLineCommandDescriptor<&Foo::Bar>(“Command2", “Command2 help")
};

for (auto editlineCommand : editLineCommands) {
// call into libedit to add editlineCommand, e.g.:
el_set(EL_ADDFN, editlineCommand.get<0>(), editLineCommand.get<1>(), editLineCommand.get<2>());
}

The pointer to member function is a type parameter because otherwise the compiler complains about the lambda needing to capture it, in which case we could not pass it to libedit.

I also plan to look into the wchar_t/char preprocessor logic that the original comment brought up but then I got distracted by shiny template stuff :wink:

Thanks!

Neal

As long as the solution matches “EditLine *” (C struct type from edit line library) to back to the C++ instance of “Editline” (lower case ell in “line” from LLDB). It should be easy to do with a template.

I am fine with any new solution that makes it easier to add new commands. I would rather have a templated function argument over a template argument if possible. I am thinking of something like:

createEditLineCommandDescriptor(“Command1", “Command1 help", &Foo::Foo1);
createEditLineCommandDescriptor(“Command2", “Command2 help", &Foo::Bar);

as I find it more readable.

Greg

Sounds great. It’ll be useful to combine the function to describe EditLine commands, help text, and their callbacks with the logic to register keyboard commands for them, as well, so I’ll do both.

However, I couldn’t make the member function pointer a function argument, because then the lambda has to capture it, which made it uncastable to the signature for a libedit callback, a C function pointer.

Thanks,

Neal