proposal: ServiceDecl

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

proposal: ServiceDecl

Daniel Boelzle

Hello,

I'd like to present/discuss a recent helper I have developed which IMO
simplifies the implementation of UNO services in C++ a lot.
Using the following (what I call) service declaration, the developer can
concentrate on implementing her service's interfaces, e.g.

class MyImpl : cppu::WeakImplHelper2<XInterface1, XInterface2> {...};

There is no need to implement lang::XServiceInfo nor
lang::XInitialization (if the service expects arguments for creation).

Next, one just has to declare the above class defining a service
declaration object:

namespace sdecl = comphelper::service_decl;
sdecl::ServiceDecl const myDecl(
    sdecl::class_<MyImpl>(),
    "org.openoffice.MyService",
    [optional "my.implementation.name"] );

The ServiceDecl ctor expects as

1st parameter: a comphelper::service_decl::class_ object as first
parameter (which I further explain shortly)

2nd parameter: a single name or a uno::Sequence<rtl::OUString> stating
the supported service names

3rd parameter (optional): an implementation name; if this has been
omitted, the implementation name will be generated out of MyImpl'S
type_info (which I will explain shortly).

The class_ object states the user's implementation class, which by
default is required to define a constructor taking a

    uno::Reference<uno::XComponentContext>

as its sole argument.  If the user requires service arguments for
construction, she can customize the class_ object, using

    sdecl::class_<MyImpl, sdecl::with_args<true> >()

In this case, the user's implementation class is required to define a
constuctor taking

    uno::Sequence<uno::Any>,
    uno::Reference<XComponentContext>

If the user needs to execute "special" post processing code, e.g. for
registering the newly created object as a listener, then she can specify
a function/functor, too:

    sdecl::class_<MyImpl, ...>(&postProcessing)

That functor needs to be of the following form, e.g.

uno::Reference<uno::XInterface> postProcessing( MyClass * p );

and gets the _yet unacquired_ pointer.

If the implementation name ought to be generated, then the mangled C++
name including the user's class is taken into account as well as the
library name, e.g. a generated name may look like:

"deployment680mi.uno.dll;.?AVPackageManagerFactoryImpl@factory@dp_manager@@"
(MSVC, using type_info::raw_name())

"deployment680li.uno.so;N10dp_manager7factory25PackageManagerFactoryImplE"
(gcc 3.4.1, using type_info::name())

There is a flaw when the user uses an anonymous namespace for the
implementation class:
- At least on Windows there is the possibility that the generated
implementation name is not unique (e.g. same anonymous class name in two
different compilation units within same dll).
- g++ mangles the whole compilation path into the anonymous symbol (+
time stamp etc.).  This will avoid the possibility to just quickly
compile and link a library with debug and exchange without
re-registering it, which I fear leads to subtle confusion.
IMO, sad but true, using a anonymous namespace is a bad idea here, so I
currently disallow this leading to a runtime error when the library is
loaded, stating these problems (currently on Windows only; easy to
detect).  Implementation classes have to be declared in a _named_
namespace (if any).

When it comes to exporting the necessary component_xxx() functions,
there are new helper functions that can deal with a variable number of
ServiceDecl objects:

extern "C" {
void SAL_CALL component_getImplementationEnvironment(
    const sal_Char ** ppEnvTypeName, uno_Environment ** )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}

sal_Bool SAL_CALL component_writeInfo(
    lang::XMultiServiceFactory * pServiceManager,
    registry::XRegistryKey * pRegistryKey )
{
    return component_writeInfoHelper(
        pServiceManager, pRegistryKey, myDecl, myDecl2, ... );
}

void * SAL_CALL component_getFactory(
    sal_Char const * pImplName,
    lang::XMultiServiceFactory * pServiceManager,
    registry::XRegistryKey * pRegistryKey )
{
    return component_getFactoryHelper(
        pImplName, pServiceManager, pRegistryKey, myDecl, myDecl2, ...);
}
} // extern "C"

By default, the component_xxxHelper() functions can cope with up to 8
declarations (can be increased using
COMPHELPER_SERVICEHELPER_COMPONENT_HELPER_MAX_ARGS before including
comphelper/servicedecl.hxx).

You can have a look at the implementation on cws dbo510 (tag
cws_src680_dbo510):
comphelper/inc/comphelper/servicedecl.hxx
comphelper/inc/comphelper/makesequence.hxx
comphelper/source/misc/servicedecl.cxx

For testing purposes, I have adopted the deployment API implementation
(module desktop) which comes with quite a lot services.

So what do you think?  Comments, please.

regards,
-Daniel

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

Thorsten Behrens
Daniel Boelzle <[hidden email]> writes:

> I'd like to present/discuss a recent helper I have developed which IMO
> simplifies the implementation of UNO services in C++ a lot.
>
> [...]
>
> So what do you think?  Comments, please.
>
Great stuff! Always having to write so much boilerplate has gotten to
my nerves.

One nit: since the automatic implementation name derivation looks kind
of fragile (and most of the time, I _do_ declare my service
implementations in anonymous namespaces) - how about disabling that
feature by default? I have the impression that not much of OOo
development outside Sun takes place on Windows, and specifying one
(more) string at the constructor is really not that much overhead...

Cheers,

--

Thorsten

If you're not failing some of the time, you're not trying hard enough.

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

Daniel Boelzle
In reply to this post by Daniel Boelzle
> One nit: since the automatic implementation name derivation looks kind
> of fragile (and most of the time, I _do_ declare my service
> implementations in anonymous namespaces) - how about disabling that
> feature by default? I have the impression that not much of OOo
> development outside Sun takes place on Windows, and specifying one
> (more) string at the constructor is really not that much overhead...

Yes, the current implementation disallows the use of anonymous
namespaces (at least on Windows, the dll cannot be registered).
I agree, it is really not much work to specify an implementation name,
but it would be nice to get rid of it.  IMO the current (though not
specified) rule inserting a "comp." into the service name to make an
implementation name, e.g.

"org.openoffice.package.MyService" =>
"org.openoffice.comp.package.MyService"

is even more fragile.

I think using a named namespace (instead of an anonymous) is not really
overhead, and, with the prepended library name, the implementation name
is unique:
- double class definitions in the same library leads to link error
- use of anonymous namespace for class_ leads to registration error

Of course, the safe solution would be a globally unique identifier
(GUID), but much more overhead.

regards,
-Daniel

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

frank.schoenheit
In reply to this post by Daniel Boelzle
Hi Daniel,

> I'd like to present/discuss a recent helper I have developed which IMO
> simplifies the implementation of UNO services in C++ a lot.
> Using the following (what I call) service declaration, the developer can
> concentrate on implementing her service's interfaces, e.g.

again, you managed to make one of my recently-introduced helper classes
(in a CWS not yet integrated) obsolete, with a much cooler solution. I
hope you're not going to make a hobby out of this :)

> class MyImpl : cppu::WeakImplHelper2<XInterface1, XInterface2> {...};
>
> There is no need to implement lang::XServiceInfo nor

Does this mean that at runtime, if an object of this class is
instantiated, it in fact does not support XServiceInfo? Or do your
factories create a wrapper around the real class, which adds this interface?
Basically, can a Basic script developer still do something like:
  barServer = createUnoService( "com.sun.star.foo.BarServer" )
  MsgBox barServer.ImplementationName
?

> You can have a look at the implementation on cws dbo510 (tag
> cws_src680_dbo510):
> comphelper/inc/comphelper/servicedecl.hxx
> comphelper/inc/comphelper/makesequence.hxx
> comphelper/source/misc/servicedecl.cxx

Those are good candicates for cppuhelper, IMO.

Thanks & Ciao
Frank

--
- Frank Schönheit, Software Engineer         [hidden email] -
- Sun Microsystems                      http://www.sun.com/staroffice -
- OpenOffice.org Database                   http://dba.openoffice.org -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

Niklas Nebel
In reply to this post by Daniel Boelzle
Daniel Boelzle wrote:
> namespace sdecl = comphelper::service_decl;
> sdecl::ServiceDecl const myDecl(
>     sdecl::class_<MyImpl>(),
>     "org.openoffice.MyService",
>     [optional "my.implementation.name"] );

If used as a global object, with a constructor that is executed when the
DLL is loaded, that won't help with start-up performance.

Niklas

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

Daniel Boelzle
In reply to this post by Daniel Boelzle

Hello Frank,

>>class MyImpl : cppu::WeakImplHelper2<XInterface1, XInterface2> {...};
>>
>>There is no need to implement lang::XServiceInfo nor
>
>
> Does this mean that at runtime, if an object of this class is
> instantiated, it in fact does not support XServiceInfo? Or do your
> factories create a wrapper around the real class, which adds this interface?
> Basically, can a Basic script developer still do something like:
>   barServer = createUnoService( "com.sun.star.foo.BarServer" )
>   MsgBox barServer.ImplementationName

I wrap around, the object of course still supports lang::XServiceInfo at
runtime; I am using cppu::ImplInheritanceHelper1<ImplT,
css::lang::XserviceInfo>.

>>You can have a look at the implementation on cws dbo510 (tag
>>cws_src680_dbo510):
>>comphelper/inc/comphelper/servicedecl.hxx
>>comphelper/inc/comphelper/makesequence.hxx
>>comphelper/source/misc/servicedecl.cxx
>
>
> Those are good candicates for cppuhelper, IMO.

Yes, I think so.  But for now, I would stick with comphelper, because
the implementation uses boost (keep UDK/SDK free of boost stuff).

regards,
-Daniel

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

Daniel Boelzle
In reply to this post by Daniel Boelzle

Hello Niklas,

>>namespace sdecl = comphelper::service_decl;
>>sdecl::ServiceDecl const myDecl(
>>    sdecl::class_<MyImpl>(),
>>    "org.openoffice.MyService",
>>    [optional "my.implementation.name"] );
>
>
> If used as a global object, with a constructor that is executed when the
> DLL is loaded, that won't help with start-up performance.

IMO it doesn't matter whether you create that data (mostly the
implementation name) at the point of loading the library or slightly
later when the UNO service needs to be instantiated.
But I agree that those ctors are executed even though not all services
may be needed from that library, e.g. at the extreme if only one of 100
is used.  Thus, it makes sense to optimize the current ctors, so they
only save the passed char pointers, late-initializing OUString/Sequence
objects when needed.  For the case of multiple supported services we
could e.g. use a separated list of service names like
"Service1;Service2;...".  All in all, I assume runtime performance is
hardly affected then.

regards,
-Daniel

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

frank.schoenheit
Hi Daniel,

> IMO it doesn't matter whether you create that data (mostly the
> implementation name) at the point of loading the library or slightly
> later when the UNO service needs to be instantiated.

It might be everything from "slightly later" to "never" (because perhaps
sevice Foo from lib A is needed, but Bar isn't at all).

I used to use the following pattern to prevent this:

- implement
  extern "C" void SAL_CALL createServiceInfo_Foo()
  {
    static sdecl::ServiceDecl const myDecl(
      sdecl::class_<MyImpl>(),
      "org.openoffice.Foo", "org.openoffice.comp.Foo" );
  }
  beside my service implementation
- call createServiceInfo_Foo from within component_* functions, for
  every implementation in the library

This is slightly more uncomfortable than a global variable, but should
prevent all its drawbacks.

Ciao
Frank

--
- Frank Schönheit, Software Engineer         [hidden email] -
- Sun Microsystems                      http://www.sun.com/staroffice -
- OpenOffice.org Database                   http://dba.openoffice.org -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]

Reply | Threaded
Open this post in threaded view
|

Re: proposal: ServiceDecl

Juergen Schmidt-3
In reply to this post by Daniel Boelzle
Thorsten Behrens wrote:

> Daniel Boelzle <[hidden email]> writes:
>
>
>>I'd like to present/discuss a recent helper I have developed which IMO
>>simplifies the implementation of UNO services in C++ a lot.
>>
>>[...]
>>
>>So what do you think?  Comments, please.
>>
>
> Great stuff! Always having to write so much boilerplate has gotten to
> my nerves.

Of course it's a cool feature but i want to mention that the new
skeletonmaker tool can help a lot to reduce the coding effort for UNO
objects and components. The tool is not officially announced because
there is still some work to do but you can start using it and give feedback.

dump mode:
- the tools can dump method declaration of interfaces and all supported
interfaces of services to stdout so that you can easily copy this stuff
in your source file.

- it can generate complete method bodies to stdout (default return,
forwarding, composition, inline or with classname, ...)

component mode:
- it can generate complete component skeletons (buidlable, deployable)
and you have to concentrate on the real implementation only


Supported languages Java and C++

More information will follow soon

Juergen



>
> One nit: since the automatic implementation name derivation looks kind
> of fragile (and most of the time, I _do_ declare my service
> implementations in anonymous namespaces) - how about disabling that
> feature by default? I have the impression that not much of OOo
> development outside Sun takes place on Windows, and specifying one
> (more) string at the constructor is really not that much overhead...
>
> Cheers,
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]