Commits

Ronald Oussoren committed 70a1738

Remove the documentation for the C-API from users.txt (not that there was
a lot of documentation). Add a new file containing more elaborate
documentation.

  • Participants
  • Parent commits 0b44781

Comments (0)

Files changed (6)

File pyobjc/Doc/C-API.html

+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>
+Documentation for the PyObjC C-API (Preliminary)</title>
+</head>
+<body>
+<h2>Documentation for the PyObjC C-API (Preliminary)</h2>
+<h2><a name="introduction">Introduction</a></h2>
+<p><i>WARNING: This API is unstable and might change in the future.</i></p>
+<p>The PyObjC package can be extended in C (or more likely Objective-C) using
+the C API described in this document. This API should be used to write
+custom wrappers for &quot;hard&quot; methods and to create/access Objective-C proxy
+objects from the wrappers for C functions.</p>
+<p>IMHO this API shouldn't be used to write modules that &quot;just happen&quot; to
+work with Objective-C objects, using (static) methods in a class is much
+more convenient.</p>
+<p>The C API is defined in <code><span>pyobjc-api.h</span></code>. This file is currently not installed
+because the API is not entirely stable.</p>
+<p>XXX: Insert reference to the CodeGeneration scripts.</p>
+<h2><a name="limititations">Limititations</a></h2>
+<p>An important limitation of the current C API is that you can only use the API
+from one C file in the implementation of an extension module. This limitation
+will probably not be removed in future versions of the API.</p>
+<h2><a name="initialization">Initialization</a></h2>
+<p>The initialiazation function (below) should be called before using the
+rest of the API:</p>
+<pre>
+static int PyObjC_ImportAPI(PyObject* calling_module)
+</pre>
+<p>This module will return 0 if loading the module was successfull, and -1
+otherwise. Reasons for failure include: not being able to locate the module
+and API version conflicts.</p>
+<p>Loading the API will make it impossible to unload the <code><span>calling_module</span></code>.</p>
+<h2><a name="compatibility-macros">Compatibility Macros</a></h2>
+<p>On MacOS X, the version guard macro <code><span>MAC_OS_X_VERSION_MAX_ALLOWED</span></code> will 
+always be available.</p>
+<p>The macros <code><span>PyDoc_STR</span></code>, <code><span>PyDoc_VAR</span></code> and <code><span>PyDoc_STRVAR</span></code> are defined 
+when they are not defined in <code><span>Python.h</span></code>.</p>
+<h2><a name="types">Types</a></h2>
+<pre>
+PyObjCObject_Type
+
+int PyObjCObject_Check(value);
+</pre>
+<p><code><span>PyObjCObject_Type</span></code> is the type of Objective-C objects, both pure Objective-C
+objects and hybrid Python/Objective-C objects are instances of this type. Use
+<code><span>PyObjCObject_Check</span></code> to check if a value is an instance of this type.</p>
+<p>There is at most 1 proxy for an Objective-C instance. That is, you can use
+the <code><span>is</span></code> operator in Python to check if two variables refer to the same
+Objective-C object.</p>
+<pre>
+PyObjCClass_Type
+
+int PyObjCClass_Check(value);
+</pre>
+<p><code><span>PyObjCClass_Type</span></code> is the type of Objective-C classes, both pure Objective-C
+objects and hybrid Python/Objective-C classes are instances of this type. Use
+<code><span>PyObjCClass_Check</span></code> to check if a value is an instance of this type.</p>
+<p>There is at most 1 class proxy for an Objective-C class. That is, you can use
+the <code><span>is</span></code> operator in Python to compare two classes for equality.</p>
+<pre>
+PyObjCSelector_Type
+
+int PyObjCSelector_Check(value);
+</pre>
+<p><code><span>PyObjCSelector_Type</span></code> is the type of Objective-C methods (including the
+methods defined in Python).  Use <code><span>PyObjCSelector_Check</span></code> to check if a value 
+is an instance of this type.</p>
+<h2><a name="api-functions">API functions</a></h2>
+<pre>
+int PyObjC_RegisterMethodMapping(
+                     Class cls, 
+                     SEL sel, 
+                     PyObject *(callObjC)(PyObject*, PyObject*, PyObject*),
+                     IMP callPython);
+</pre>
+<p>Register a custom wrapper for a specific method. Returns -1 on failure.</p>
+<pre>
+int PyObjC_RegisterSignatureMapping(
+                     char* typespec,
+                     PyObject *(*callObjC)(PyObject*, PyObject*, PyObject*),
+                     IMP callPython);
+</pre>
+<p>Register a custom wrapper for methods with a specific signature. Returns -1
+on failure.</p>
+<pre>
+id PyObjCObject_GetObject(PyObject* obj);
+</pre>
+<p>Return the Objective-C object that is proxied by a <code><span>PyObjCObject_Type</span></code> 
+instance.</p>
+<pre>
+void PyObjCObject_ClearObject(PyObject* obj);
+</pre>
+<p>Clear the proxied object. That is, the <code><span>PyObjCObject_Type</span></code> instance will
+no longer be a proxy.</p>
+<pre>
+Class PyObjCClass_GetClass(PyObject* cls);
+</pre>
+<p>Extract the Class from a proxied Objective-C class.</p>
+<pre>
+PyObject* PyObjCClass_New(Class cls);
+</pre>
+<p>Create or find a proxy object for the class.</p>
+<pre>
+id PyObjC_PythonToId(PyObject* value);
+</pre>
+<p>Create a proxy for the Python object. This will unwrap proxied Objective-C 
+objects, and will create the appropriate proxy for Python objects.</p>
+<pre>
+PyObject* IdToPython(id value);
+</pre>
+<p>Create a proxy for the Objective-C object. This will unwrap proxied Python
+objects and will create a proxy object for Objective-C objects.</p>
+<pre>
+void PyObjCErr_FromObjC(NSException* localException);
+</pre>
+<p>Convert an Objective-C exception to Python. Use 
+<code><span>PyObjCErr_FromObjC(localException)</span></code> to convert the exception in an 
+<code><span>NS_HANDLER</span></code> block.</p>
+<p>Note that PyObjC supports roundtripping for exceptions, if the current 
+Objective-C exception is an converted Python exception, the original Python
+exception will be rethrown.</p>
+<pre>
+void PyObjCErr_ToObjC(void);
+</pre>
+<p>Convert a Python exception to Objective-C. This function does not return.</p>
+<p>Note that PyObjC supports roundtripping for exceptions, if the current Python
+exception is an converted Objective-C exception, the original Objective-C
+exception will be rethrown.</p>
+<pre>
+int PyObjC_PythonToObjC(const char* typespec, PyObject* value, void* buffer);
+</pre>
+<p>Convert the value to an Objective-C value of type <code><span>typespec</span></code>. The buffer must
+be at least <code><span>PyObjC_SizeOfType(typespec)</span></code> bytes long.</p>
+<p>NOTE: The <code><span>typespec</span></code> is a type specifier as described in the runtime 
+reference of the Objective-C manual from Apple. Use <code><span>@encode(mytype)</span></code> if to
+get code that is portable to a different Objective-C runtime.</p>
+<pre>
+PyObject* PyObjC_ObjCToPython(const char* typespec, void* value);
+</pre>
+<p>Convert an Objective-C value of type <code><span>typespec</span></code> to python.</p>
+<pre>
+PyObject* PyObjC_CallPython(id self, SEL sel, PyObject* arglist, int* isAlloc);
+</pre>
+<p>Call the Python implementation of method <code><span>sel</span></code> of <code><span>self</span></code>. The <code><span>arglist</span></code>
+must contain the complete argument list, including self. If <code><span>isAlloc</span></code> is not
+<code><span>NULL</span></code> it is used to output whether this method should return a new reference
+(TRUE) or a borrowed reference (FALSE).</p>
+<pre>
+int PyObjC_SizeOfType(const char* typespec);
+</pre>
+<p>Return the size of variables of the specified type.</p>
+<pre>
+Class PyObjCSelector_GetClass(PyObject* sel);
+</pre>
+<p>Return the class containing the definition of <code><span>sel</span></code>.</p>
+<pre>
+SEL PyObjCSelector_GetSelector(PyObject* sel);
+</pre>
+<p>Return the Objective-C method name for <code><span>sel</span></code>.</p>
+<pre>
+int PyObjCBool_Check(PyObject* obj);
+</pre>
+<p>Check if <code><span>obj</span></code> is a boolean object (either the python bool type in Python
+2.3 or the PyObjC bool type in Python 2.2)</p>
+<pre>
+PyObject* PyObjCBool_FromLong(long i);
+</pre>
+<p>Create a new bool object. This will return a Python bool object in Python 2.3
+(and later) and the PyObjC bool type in Python 2.2.</p>
+<pre>
+void PyObjC_InitSuper(struct objc_super*, Class, id);
+</pre>
+<p>Initialize the <code><span>struct</span> <span>objc_super</span></code> for use with <code><span>objc_sendMsgSuper</span></code>. Use 
+this if the <code><span>self</span></code> argument is a normal object.</p>
+<pre>
+void PyObjC_InitSuperCls(struct objc_super*, Class, Class);
+</pre>
+<p>Initialize the <code><span>struct</span> <span>objc_super</span></code> for use with <code><span>objc_sendMsgSuper</span></code>. Use 
+this if the <code><span>self</span></code> argument is a Class.</p>
+<pre>
+int  PyObjCPointerWrapper_Register(
+              const char* typespec, PyObject* (*pythonify)(void*),
+              int (*depythonify)(PyObject*, void*)
+      );
+</pre>
+<p>Use <code><span>pythonify</span></code> to convert pointers of type <code><span>typespec</span></code> to python and
+<code><span>depythonify</span></code> to extract them from Python. Use this to register helper 
+function for the conversion of opaque pointers.</p>
+<pre>
+id  PyObjCUnsupportedMethod_IMP(id, SEL);
+</pre>
+<p>Use this as an argument for <code><span>PyObjC_RegisterMethodMapping</span></code> or 
+<code><span>PyObjC_RegisterSignatureMapping</span></code> if the method is not callable from 
+Objective-C.</p>
+<pre>
+PyObject* PyObjCUnsupportedMethod_Caller(PyObject*, PyObject*, PyObject*);
+</pre>
+<p>Use this as an argument for <code><span>PyObjC_RegisterMethodMapping</span></code> or 
+<code><span>PyObjC_RegisterSignatureMapping</span></code> if the method is not callable from Python.</p>
+<pre>
+int PyObjCObject_Convert(PyObject* object, void* pvar);
+</pre>
+<p>This is a variation on <code><span>PyObjC_PythonToId</span></code> than can be used with 
+<code><span>PyArg_Parse</span></code>.</p>
+</body>
+</html>

File pyobjc/Doc/C-API.txt

+================================================
+Documentation for the PyObjC C-API (Preliminary)
+================================================
+
+Introduction
+------------
+
+*WARNING: This API is unstable and might change in the future.*
+
+The PyObjC package can be extended in C (or more likely Objective-C) using
+the C API described in this document. This API should be used to write
+custom wrappers for "hard" methods and to create/access Objective-C proxy
+objects from the wrappers for C functions.
+
+IMHO this API shouldn't be used to write modules that "just happen" to
+work with Objective-C objects, using (static) methods in a class is much
+more convenient.
+
+The C API is defined in ``pyobjc-api.h``. This file is currently not installed
+because the API is not entirely stable.
+
+XXX: Insert reference to the CodeGeneration scripts.
+
+Limititations
+-------------
+
+An important limitation of the current C API is that you can only use the API
+from one C file in the implementation of an extension module. This limitation
+will probably not be removed in future versions of the API.
+
+Initialization
+--------------
+
+The initialiazation function (below) should be called before using the
+rest of the API::
+
+  static int PyObjC_ImportAPI(PyObject* calling_module)
+
+This module will return 0 if loading the module was successfull, and -1
+otherwise. Reasons for failure include: not being able to locate the module
+and API version conflicts.
+
+Loading the API will make it impossible to unload the ``calling_module``.
+
+Compatibility Macros
+--------------------
+
+On MacOS X, the version guard macro ``MAC_OS_X_VERSION_MAX_ALLOWED`` will 
+always be available. 
+
+The macros ``PyDoc_STR``, ``PyDoc_VAR`` and ``PyDoc_STRVAR`` are defined 
+when they are not defined in ``Python.h``.
+
+Types
+-----
+
+::
+ 
+   PyObjCObject_Type
+
+   int PyObjCObject_Check(value);
+
+``PyObjCObject_Type`` is the type of Objective-C objects, both pure Objective-C
+objects and hybrid Python/Objective-C objects are instances of this type. Use
+``PyObjCObject_Check`` to check if a value is an instance of this type.
+
+There is at most 1 proxy for an Objective-C instance. That is, you can use
+the ``is`` operator in Python to check if two variables refer to the same
+Objective-C object.
+
+::
+ 
+   PyObjCClass_Type
+
+   int PyObjCClass_Check(value);
+
+``PyObjCClass_Type`` is the type of Objective-C classes, both pure Objective-C
+objects and hybrid Python/Objective-C classes are instances of this type. Use
+``PyObjCClass_Check`` to check if a value is an instance of this type.
+
+There is at most 1 class proxy for an Objective-C class. That is, you can use
+the ``is`` operator in Python to compare two classes for equality.
+
+::
+ 
+   PyObjCSelector_Type
+
+   int PyObjCSelector_Check(value);
+
+``PyObjCSelector_Type`` is the type of Objective-C methods (including the
+methods defined in Python).  Use ``PyObjCSelector_Check`` to check if a value 
+is an instance of this type.
+
+API functions
+-------------
+
+::
+
+   int PyObjC_RegisterMethodMapping(
+			Class cls, 
+			SEL sel, 
+			PyObject *(callObjC)(PyObject*, PyObject*, PyObject*),
+			IMP callPython);
+
+Register a custom wrapper for a specific method. Returns -1 on failure.
+
+::
+
+   int PyObjC_RegisterSignatureMapping(
+			char* typespec,
+			PyObject *(*callObjC)(PyObject*, PyObject*, PyObject*),
+			IMP callPython);
+
+Register a custom wrapper for methods with a specific signature. Returns -1
+on failure.
+
+::
+
+  id PyObjCObject_GetObject(PyObject* obj);
+
+Return the Objective-C object that is proxied by a ``PyObjCObject_Type`` 
+instance.
+
+::
+
+  void PyObjCObject_ClearObject(PyObject* obj);
+
+Clear the proxied object. That is, the ``PyObjCObject_Type`` instance will
+no longer be a proxy.
+
+::
+
+  Class PyObjCClass_GetClass(PyObject* cls);
+
+Extract the Class from a proxied Objective-C class. 
+
+::
+
+  PyObject* PyObjCClass_New(Class cls);
+
+Create or find a proxy object for the class. 
+
+::
+  
+  id PyObjC_PythonToId(PyObject* value);
+
+Create a proxy for the Python object. This will unwrap proxied Objective-C 
+objects, and will create the appropriate proxy for Python objects.
+
+::
+
+  PyObject* IdToPython(id value);
+
+Create a proxy for the Objective-C object. This will unwrap proxied Python
+objects and will create a proxy object for Objective-C objects.
+
+::
+
+  void PyObjCErr_FromObjC(NSException* localException);
+
+Convert an Objective-C exception to Python. Use 
+``PyObjCErr_FromObjC(localException)`` to convert the exception in an 
+``NS_HANDLER`` block.
+
+Note that PyObjC supports roundtripping for exceptions, if the current 
+Objective-C exception is an converted Python exception, the original Python
+exception will be rethrown.
+
+::
+
+  void PyObjCErr_ToObjC(void);
+
+Convert a Python exception to Objective-C. This function does not return.
+
+Note that PyObjC supports roundtripping for exceptions, if the current Python
+exception is an converted Objective-C exception, the original Objective-C
+exception will be rethrown.
+
+::
+
+  int PyObjC_PythonToObjC(const char* typespec, PyObject* value, void* buffer);
+
+Convert the value to an Objective-C value of type ``typespec``. The buffer must
+be at least ``PyObjC_SizeOfType(typespec)`` bytes long.
+
+NOTE: The ``typespec`` is a type specifier as described in the runtime 
+reference of the Objective-C manual from Apple. Use ``@encode(mytype)`` if to
+get code that is portable to a different Objective-C runtime.
+
+::
+
+  PyObject* PyObjC_ObjCToPython(const char* typespec, void* value);
+
+Convert an Objective-C value of type ``typespec`` to python. 
+
+::
+
+  PyObject* PyObjC_CallPython(id self, SEL sel, PyObject* arglist, int* isAlloc);
+
+Call the Python implementation of method ``sel`` of ``self``. The ``arglist``
+must contain the complete argument list, including self. If ``isAlloc`` is not
+``NULL`` it is used to output whether this method should return a new reference
+(TRUE) or a borrowed reference (FALSE).
+
+::
+
+  int PyObjC_SizeOfType(const char* typespec);
+
+Return the size of variables of the specified type.
+
+::
+
+  Class PyObjCSelector_GetClass(PyObject* sel);
+
+Return the class containing the definition of ``sel``.
+
+::
+
+  SEL PyObjCSelector_GetSelector(PyObject* sel);
+
+Return the Objective-C method name for ``sel``.
+
+::
+
+  int PyObjCBool_Check(PyObject* obj);
+
+Check if ``obj`` is a boolean object (either the python bool type in Python
+2.3 or the PyObjC bool type in Python 2.2)
+
+::
+
+  PyObject* PyObjCBool_FromLong(long i);
+
+Create a new bool object. This will return a Python bool object in Python 2.3
+(and later) and the PyObjC bool type in Python 2.2.
+
+::
+
+  void PyObjC_InitSuper(struct objc_super*, Class, id);
+
+Initialize the ``struct objc_super`` for use with ``objc_sendMsgSuper``. Use 
+this if the ``self`` argument is a normal object.
+
+::
+
+  void PyObjC_InitSuperCls(struct objc_super*, Class, Class);
+
+Initialize the ``struct objc_super`` for use with ``objc_sendMsgSuper``. Use 
+this if the ``self`` argument is a Class.
+
+::
+
+  int  PyObjCPointerWrapper_Register(
+	        const char* typespec, PyObject* (*pythonify)(void*),
+		int (*depythonify)(PyObject*, void*)
+	);
+
+Use ``pythonify`` to convert pointers of type ``typespec`` to python and
+``depythonify`` to extract them from Python. Use this to register helper 
+function for the conversion of opaque pointers.
+
+::
+
+  id  PyObjCUnsupportedMethod_IMP(id, SEL);
+
+Use this as an argument for ``PyObjC_RegisterMethodMapping`` or 
+``PyObjC_RegisterSignatureMapping`` if the method is not callable from 
+Objective-C.
+
+::
+
+  PyObject* PyObjCUnsupportedMethod_Caller(PyObject*, PyObject*, PyObject*);
+
+Use this as an argument for ``PyObjC_RegisterMethodMapping`` or 
+``PyObjC_RegisterSignatureMapping`` if the method is not callable from Python.
+
+::
+
+  int PyObjCObject_Convert(PyObject* object, void* pvar);
+
+This is a variation on ``PyObjC_PythonToId`` than can be used with 
+``PyArg_Parse``.

File pyobjc/Doc/api-notes-macosx.html

 </ul>
 <h3><a href="#id16" name="class-nsopenglpixelformat">Class <code><span>NSOpenGLPixelFormat</span></code></a></h3>
 <ul>
-<li><code><span>getValues:forAttribute:forVirtualScreen:</span></code>
-This method is not yet supported</li>
-<li><code><span>initWithAttributes:</span></code>
-This method is not yet supported</li>
+<li><code><span>getValues:forAttribute:forVirtualScreen:</span></code><p>This method is not yet supported</p>
+</li>
+<li><code><span>initWithAttributes:</span></code><p>This method is not yet supported</p>
+</li>
 </ul>
 <h3><a href="#id17" name="class-nsquickdrawview">Class <code><span>NSQuickDrawView</span></code></a></h3>
 <ul>
-<li><code><span>qdPort</span></code>
-This method is not yet supported and will return a MacPython wrapper for
-a QuickDraw port in the future.</li>
+<li><code><span>qdPort</span></code><p>This method returns an instance from a type Carbon.QuickDraw. This 
+requires MacPython.</p>
+</li>
 </ul>
 <h3><a href="#id18" name="class-nssimplehorizontaltypesetter">Class <code><span>NSSimpleHorizontalTypesetter</span></code></a></h3>
 <ul>
 <p>The following methods are not supported in the current version of PyObjC.
 This limitation will be lifted in a future version of the bridge.</p>
 <ul>
-<li><code><span>encodeBytes:length:</span></code></li>
 <li><code><span>decodeBytesWithReturnedLength:</span></code></li>
-<li><code><span>encodeValuesOfObjCType:</span></code></li>
-<li><code><span>decodeValuesOfObjCType:</span></code></li>
 <li><code><span>decodeBytesForKey:returnedLength:</span></code></li>
-<li><code><span>decodeBytesWithoutReturnedLength:</span></code></li>
+<li><code><span>encodeValuesOfObjCType:</span></code><p>Use multiple calls to <code><span>encodeValueOfObjCType:at:</span></code> instead.</p>
+</li>
+<li><code><span>decodeValuesOfObjCType:</span></code><p>Use multiple calls to <code><span>decodeValueOfObjCType:at:</span></code> instead. Note that
+that won't work if your trying to read back data that was written using
+<code><span>encodeValuesOfObjCType:</span></code>.</p>
+</li>
 </ul>
+<p>The method <code><span>decodeBytesWithoutReturnedLength:</span></code> is not supported, use 
+<code><span>decodeBytesWithReturnedLength:</span></code> instead. It is not possible to safely
+represent the return value of this method in Python.</p>
 <h3><a href="#id25" name="class-nsdata">Class <code><span>NSData</span></code></a></h3>
 <ul>
 <li><code><span>initWithBytesNoCopy:length:</span></code>

File pyobjc/Doc/users.html

 create an NSAutoreleasePool for the thread. This has not been tested. The
 Cocoa threading classes should not be used because they don't update the 
 state of the Python interpreter when creating a new thread.</p>
-<h2><a name="c-api">C API</a></h2>
-<h3><a name="id1">Introduction</a></h3>
-<p>The PyObjC package provides a C API that can be used when you're wrapping 
-functions that deal with Objective-C objects or classes. It can also be used
-to provide functions that help to wrap problematic objective-C methods (like 
-those that take a variable number of arguments).</p>
-<p>This API is used by the 'Cocoa' package (part of the PyObjC distribution) to
-wrap the entire Cocoa API.</p>
-<h3><a name="how-to-use-it">How to use it</a></h3>
-<p>You <code><span>#include</span></code> &quot;pyobjc-api.h&quot; in your module implementation. In the module
-initialisation function you then call <code><span>ObjC_ImportModule(mymodule)</span></code>. After
-this you can use the functions and constants defined in the API.</p>
-<h3><a name="constants">Constants</a></h3>
-<p>TODO</p>
-<h3><a name="functions">Functions</a></h3>
-<p>TODO</p>
-<h3><a name="id2">Limitations</a></h3>
-<p>We currently assume that extension module contain at most 1 file that uses
-the PyObjC API.</p>
 </body>
 </html>

File pyobjc/Doc/users.txt

 create an NSAutoreleasePool for the thread. This has not been tested. The
 Cocoa threading classes should not be used because they don't update the 
 state of the Python interpreter when creating a new thread.
-
-C API
------
-
-Introduction
-~~~~~~~~~~~~
-
-The PyObjC package provides a C API that can be used when you're wrapping 
-functions that deal with Objective-C objects or classes. It can also be used
-to provide functions that help to wrap problematic objective-C methods (like 
-those that take a variable number of arguments).
-
-This API is used by the 'Cocoa' package (part of the PyObjC distribution) to
-wrap the entire Cocoa API.
-
-How to use it
-~~~~~~~~~~~~~
-
-You ``#include`` "pyobjc-api.h" in your module implementation. In the module
-initialisation function you then call ``ObjC_ImportModule(mymodule)``. After
-this you can use the functions and constants defined in the API.
-
-Constants
-~~~~~~~~~
-
-TODO
-
-Functions
-~~~~~~~~~
-
-TODO
-
-
-Limitations
-~~~~~~~~~~~
-
-We currently assume that extension module contain at most 1 file that uses
-the PyObjC API.

File pyobjc/Doc/website.lst

 classes.txt,developer,30
 libffi.txt,developer,30
 structure.txt,developer,20
+C-API.txt,developer,15