[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kreport/nonvisual-T517-staniek] tools: SDC 0.2: add support for explicit sharing (implicit is still
From:       Jaroslaw Staniek <staniek () kde ! org>
Date:       2015-08-01 0:04:27
Message-ID: E1ZLKHr-00044c-6O () scm ! kde ! org
[Download RAW message or body]

Git commit e4f0d650b94884ea02bc48c0f0084a941b3ea1d3 by Jaroslaw Staniek.
Committed on 31/07/2015 at 23:35.
Pushed by staniek into branch 'nonvisual-T517-staniek'.

SDC 0.2: add support for explicit sharing (implicit is still default), inheritance \
with shared pointer

M  +145  -43   tools/sdc.py

http://commits.kde.org/kreport/e4f0d650b94884ea02bc48c0f0084a941b3ea1d3

diff --git a/tools/sdc.py b/tools/sdc.py
index ca39838..2eba0e8 100755
--- a/tools/sdc.py
+++ b/tools/sdc.py
@@ -25,7 +25,7 @@
 
 import os, sys, shlex
 
-version = '0.1'
+version = '0.2'
 line = ''
 
 def usage():
@@ -148,11 +148,24 @@ QMap<QString, QString> %s::Data::toMap() const
     Inserts generated operator==() code for shared class into the output.
 """
 def insert_operator_eq():
+    global outfile, shared_class_name, superclass
+    outfile.write("""    //! @return true if this object is equal to @a other; \
otherwise returns false. +    bool operator==(const %s& other) const {
+        return *%s == *other.%s;
+    }
+
+""" % (shared_class_name, 'data()' if superclass else 'd', 'data()' if superclass \
else 'd')) +
+"""
+    Inserts generated clone() method (makes sense for explicitly shared class).
+"""
+def insert_clone():
     global outfile, shared_class_name
-    outfile.write("""    bool operator==(const %s& other) const {
-        return *d == *other.d;
+    outfile.write("""    //! Clones the object with all attributes; the copy isn't \
shared with the original. +    %s clone() {
+        return %s(d->clone());
     }
-""" % shared_class_name)
+""" % (shared_class_name, shared_class_name))
 
 """
     Inserts generated Data::operator==() code into the output.
@@ -179,7 +192,7 @@ def insert_data_operator_eq():
     Inserts generated code into the output.
 """
 def insert_generated_code(context):
-    global infile, outfile, generated_code_inserted, data_class_ctor, \
data_class_copy_ctor +    global infile, outfile, generated_code_inserted, \
                data_class_ctor, data_class_copy_ctor, superclass
     global data_class_members, data_accesors, protected_data_accesors, main_ctor, \
shared_class_name, shared_class_options  global prev_line
     if generated_code_inserted:
@@ -197,6 +210,12 @@ def insert_generated_code(context):
         }
 
 """)
+    outfile.write("""        virtual ~Data() {}
+
+        virtual %sData* clone() const { return new Data(*this); }
+
+""" % ((superclass + '::') if superclass else ''))
+
     if shared_class_options['with_from_to_map']:
         outfile.write("""        /*! Constructor for Data object, takes attributes \
                saved to map @a map.
         If @a ok is not 0, *ok is set to true on success and to false on failure. \
@see toMap(). */ @@ -216,6 +235,8 @@ def insert_generated_code(context):
         insert_fromMap_toMap_methods()
     if shared_class_options['operator==']:
         insert_operator_eq()
+    if shared_class_options['explicit'] and not superclass:
+        insert_clone()
     if protected_data_accesors:
         outfile.write("protected:")
         outfile.write(protected_data_accesors)
@@ -314,9 +335,9 @@ def update_data_accesors():
         else:
             val = """
     %s%s %s() const {
-        return d->%s;
+        return %s->%s;
     }
-""" % (invokable, member['type'], getter, member['name'])
+""" % (invokable, member['type'], getter, 'data()' if superclass else 'd', \
member['name'])  if member['access'] == 'public':
             data_accesors += val
         else: # protected
@@ -342,9 +363,9 @@ def update_data_accesors():
         else:
             val = """
     %svoid %s(%s %s%s) {
-        d->%s = %s;
+        %s->%s = %s;
     }
-""" % (invokable, setter, arg_type, member['name'], default_setter, member['name'], \
member['name']) +""" % (invokable, setter, arg_type, member['name'], default_setter, \
'data()' if superclass else 'd', member['name'], member['name'])  if member['access'] \
== 'public':  data_accesors += val
         else: # protected
@@ -367,7 +388,7 @@ def get_shared_class_option(lst, option_name):
         lst.remove(option_name)
     return lst
 
-# like get_shared_class_option() but also gets value (not just checks for existence)
+""" like get_shared_class_option() but also gets value (not just checks for \
existence) """  def get_shared_class_option_with_value(lst, option_name):
     global shared_class_options
     for item in lst:
@@ -381,7 +402,7 @@ def get_shared_class_option_with_value(lst, option_name):
 
 def warningHeader():
     return """/****************************************************************************
                
-** Implicitly Shared Class code from reading file '%s'
+** Shared Class code from reading file '%s'
 **
 ** Created
 **      by: The Shared Data Compiler version %s
@@ -391,8 +412,8 @@ def warningHeader():
 
 """ % (get_file(in_fname), version)
 
-# generates conversion code to string from many types, used by Data::toMap()
-# @todo more types
+""" generates conversion code to string from many types, used by Data::toMap()
+    @todo more types """
 def generate_toString_conversion(name, _type):
     if _type == 'QString' or _type == 'QByteArray':
         return name
@@ -400,8 +421,8 @@ def generate_toString_conversion(name, _type):
         return 'QString::number((int)%s)' % name # 0 or 1
     return 'QVariant(%s).toString()' % name
 
-# generates conversion code from string to many types, used by Data(QMap<..>)
-# @todo more types
+""" generates conversion code from string to many types, used by Data(QMap<..>)
+    @todo more types """
 def generate_fromString_conversion(name, _type):
     s = 'map[QLatin1String(\"%s\")]' % name
     if _type == 'bool': # 0 or 1
@@ -442,6 +463,8 @@ def get_pos_for_QSharedData_h():
 
 # replaces "Foo<ABC<DEF>>" with "Foo< ABC< DEF > >" to avoid build errors
 def fix_templates(s):
+    if s.count('<') < 2:
+        return s
     result=''
     for c in s:
         if c == '>':
@@ -458,9 +481,16 @@ def other_comment(line):
       or ln .startswith('//!') \
       or ln.startswith('///')
 
+""" @return name of shared class, possibly with full 'inheriting' section name and \
superclass """ +def get_shared_class_name_and_inheritance(lst):
+    if lst[-2] == 'public' and lst[-3] == ':': # <name> : public <inherited>
+        return (lst[-4], ' ' + lst[-3] + ' ' + lst[-2] + ' ' + lst[-1], lst[-1])
+    else:
+        return (lst[-1], '', '') # <name>
+
 def process():
     global infile, outfile, generated_code_inserted, data_class_ctor, \
                data_class_copy_ctor
-    global shared_class_name, shared_class_options, shared_class_inserted, \
data_class_members +    global shared_class_name, superclass, shared_class_options, \
                shared_class_inserted, data_class_members
     global members_list, data_accesors, member, main_ctor, toMap_impl, fromMap_impl
     global prev_line, line
     outfile.write(warningHeader())
@@ -512,23 +542,37 @@ def process():
             # output: class <EXPORT> <NAME>
             export = param(lst, 'export')
             inherits = param(lst, 'inherits')
+            lst = get_shared_class_option(lst, 'explicit')
             lst = get_shared_class_option(lst, 'operator==')
             lst = get_shared_class_option(lst, 'with_from_to_map')
             lst = get_shared_class_option(lst, 'virtual_dtor')
             lst = get_shared_class_option_with_value(lst, 'namespace')
-            shared_class_name = lst[-1]
+            (shared_class_name, shared_class_inheritance, superclass) = \
get_shared_class_name_and_inheritance(lst) +            if superclass:
+                shared_class_options['virtual_dtor'] = True # inheritance implies \
this  main_ctor = """    };
 
     %s()
-     : d(new Data)
+     : %s(new Data)
     {
     }
 
     %s(const %s& other)
-     : d(other.d)
+     : %s
     {
     }
-""" % (shared_class_name, shared_class_name, shared_class_name)
+""" % (shared_class_name, superclass if superclass else 'd', shared_class_name, \
shared_class_name, (superclass + '(other)') if superclass else 'd(other.d)') +        \
if superclass: +                main_ctor += """
+    %s(const %s& other)
+     : %s(other)
+    {
+        if (!data()) { // '@a 'other' does not store suitable data, create a new one \
and copy what we have +            d = new Data(*d.data());
+        }
+    }
+""" % (shared_class_name, superclass, superclass)
+
             if shared_class_options['with_from_to_map']:
                 main_ctor += """
     /*! Constructor for %s object, takes attributes saved to map @a map.
@@ -542,9 +586,23 @@ def process():
     %s~%s();
 """ % (('virtual ' if shared_class_options['virtual_dtor'] else ''), \
shared_class_name)  if export:
-                name = export + ' ' + shared_class_name
+                name = export + ' ' + shared_class_name + shared_class_inheritance
             if inherits:
                 inherits = ' : ' + inherits
+            if shared_class_options['explicit']:
+                outfile.write("""//! @note objects of this class are explicitly \
shared, what means they behave like regular +//!       C++ pointers, except that by \
doing reference counting and not deleting the shared +//!       data object until the \
reference count is 0, they avoid the dangling pointer problem. +//!       See <a \
href="http://doc.qt.io/qt-5/qexplicitlyshareddatapointer.html#details">Qt \
documentation</a>. +//!
+""")
+            else:
+                outfile.write("""//! @note objects of this class are implicitly \
shared, what means they have value semantics +//! by offering copy-on-write behaviour \
to maximize resource usage and minimize copying. +//! Only a pointer to the data is \
passed around. See <a href="http://doc.qt.io/qt-5/qshareddatapointer.html#details">Qt \
documentation</a>. +//!
+""")
+
             outfile.write("class %s%s\n" % (name, inherits))
             while True:
                 prev_line = line
@@ -603,16 +661,16 @@ def process():
             if lst[-1].endswith(';'):
                 lst[-1] = lst[-1][:-1]
             #print lst
-            # syntax: data_member TYPE NAME [default=DEFAULT_VALUE]
-            #                               [no_getter] [getter=CUSTOM_GETTER_NAME]
-            #                               [custom]
-            #                               [custom_getter]
-            #                               [default_setter=DEFAULT_SETTER'S_PARAM]
-            #                               [no_setter] [setter=CUSTOM_SETTER_NAME]
-            #                               [custom_setter]
-            #                               [mutable] [simple_type]
-            #                               [invokable]
-            # output: getter, setter methods, data memeber
+            """ syntax: data_member TYPE NAME [default=DEFAULT_VALUE]
+                                              [no_getter] \
[getter=CUSTOM_GETTER_NAME] +                                              [custom]
+                                              [custom_getter]
+                                              \
[default_setter=DEFAULT_SETTER'S_PARAM] +                                             \
[no_setter] [setter=CUSTOM_SETTER_NAME] +                                             \
[custom_setter] +                                              [mutable] \
[simple_type] +                                              [invokable] [explicit]
+            output: getter, setter methods, data memeber """
             if lst[0] == 'data_method':
                 #if member.has_key('docs'):
                 #    data_class_members += member['docs'] + '\n'
@@ -641,17 +699,26 @@ def process():
             member['invokable'] = param_exists(lst, 'invokable')
             #print member
             if not data_class_ctor_changed:
-                data_class_ctor = """    //! Internal data class used to implement \
                implicitly shared class %s.\n    //! Provides thread-safe reference \
                counting.
-    class Data : public QSharedData
+                data_class_ctor = """    //! Internal data class used to implement \
%s shared class %s. +    //! Provides thread-safe reference counting.
+    class Data : public %s
     {
     public:
         Data()
-""" % shared_class_name
+""" % ('explicitly' if shared_class_options['explicit'] else 'implicitly', \
shared_class_name, (superclass + '::Data') if superclass else 'QSharedData')  if not \
                data_class_copy_ctor_changed:
-                data_class_copy_ctor = """
+                data_class_copy_ctor = ''
+                if superclass:
+                    data_class_copy_ctor += """
+        Data(const %s::Data& other)
+         : %s::Data(other)
+        {
+        }
+""" % (superclass, superclass)
+                data_class_copy_ctor += """
         Data(const Data& other)
-        : QSharedData(other)
-"""
+         : %s(other)
+""" % ((superclass + '::Data') if superclass else 'QSharedData')
                 data_class_copy_ctor_changed = True
             if member['default']:
                 data_class_ctor += '        '
@@ -662,15 +729,18 @@ def process():
                     data_class_ctor_changed = True
                 data_class_ctor += member['name'] + '(' + member['default'] + ')\n'
 #            print data_class_ctor
-            data_class_copy_ctor += '        , %s(other.%s)\n' % (member['name'], \
member['name']) +            data_class_copy_ctor += '         , %s(other.%s)\n' % \
(member['name'], member['name'])  if member.has_key('docs'):
                 data_class_members += member['docs']
 
             isInternalMember = member['no_getter'] and member['no_setter']
+            mutable = 'mutable ' if member['mutable'] else ''
+            data_class_members += "        %s%s %s;" % (mutable, member['type'], \
member['name']) +            # add doc for shared data member
             if isInternalMember:
-                data_class_members += "        //! @internal"
+                data_class_members += " //!< @internal"
             else:
-                data_class_members += "        //! @see "
+                data_class_members += " //!< @see "
             if not member['no_getter']:
                 getter = member['getter']
                 if not getter:
@@ -682,8 +752,7 @@ def process():
                 setter = makeSetter(member['name'], member['setter'])
                 data_class_members += "%s::%s()" % (shared_class_name, setter)
             data_class_members += "\n"
-            mutable = 'mutable ' if member['mutable'] else ''
-            data_class_members += "        %s%s %s;\n" % (mutable, member['type'], \
member['name']) +
             if shared_class_options['with_from_to_map']:
                 toMap_impl += '    map[QLatin1String(\"%s\")] = %s;\n' % \
                (member['name'], generate_toString_conversion(member['name'], \
                member['type']))
                 fromMap_impl += '    %s\n' % \
generate_fromString_conversion(member['name'], member['type']) @@ -696,8 +765,41 @@ \
def process():  insert_generated_code(5)
 #            outfile.write('\nprivate:\n');
             outfile.write('\nprotected:\n');
-            outfile.write('    QSharedDataPointer<Data> d;\n');
+            if shared_class_options['explicit']:
+                if superclass:
+                    outfile.write("""    virtual const Data* data() const { return \
dynamic_cast<const Data*>(%s::d.constData()); } +    virtual Data* data() { return \
dynamic_cast<Data*>(%s::d.data()); } +""" % (superclass, superclass))
+                else:
+                    outfile.write("""    %s(Data *data)
+     : d(data)
+    {
+    }
+
+    %s(QExplicitlySharedDataPointer<%s::Data> &data)
+     : d(data)
+    {
+    }
+
+    QExplicitlySharedDataPointer<Data> d;
+""" % (shared_class_name, shared_class_name, shared_class_name))
+            else:
+                outfile.write('    QSharedDataPointer<Data> d;\n');
             outfile.write(line)
+
+            if shared_class_options['explicit']:
+                outfile.write("""
+template<>
+%s %s::Data *QSharedDataPointer<%s::Data>::clone();
+""" % (export, shared_class_name, shared_class_name))
+                open_sdc()
+                outfile_sdc.write("""template<>
+%s %s::Data *QSharedDataPointer<%s::Data>::clone()
+{
+    return d->clone();
+}
+""" % (export, shared_class_name, shared_class_name))
+
         else:
             #outfile.write('____ELSE____\n');
             if False and other_comment(line):


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic