C++11 in Qt 5: Challenges & Solutions Thiago Macieira, Qt Core Maintainer Aspen, May 2013

Who am I? • Open Source developer for 15 years • C++ developer for 13 years • Software Architect at Intel’s Open Source Technology Center (OTC) • Maintainer of two modules in the Qt Project ‒ QtCore and QtDBus

• MBA and double degree in Engineering • Previously, led the “Qt Open Governance” project

2

© 2013 Intel, 2012 KDAB

Qt 5 First major version in 7 years Goals:

Release status:

• New graphics stack

• Qt 5.0.2 released in April

• Declarative UI design with QML

• Qt 5.1.0 beta 1 released in May 14, 2013

• More modular for quicker releases • New, modern features • Mostly source-compatible w/ Qt 4

3

© 2013 Intel, 2012 KDAB

The C++11 challenge • We would have liked to switch

“C++98 costs more” • But we need to maintain compatibility ‒ MSVC 2008 ‒ GCC 4.2 ‒ Commercial Unix compilers (AIX and Solaris)

4

© 2013 Intel, 2012 KDAB

A look at C++11 in Qt 5.1 • Added a lot of C++11 support to Qt 4.8, Qt 5.0 and Qt 5.1 • Lots of C++11 stuff left to do for 5.2: ‒ Move Semantics (containers and containees) ‒ add constexpr to more classes / functions ‒ 'explicit' missing on N-ary ctors, N ≥ 2

5

© 2013 Intel, 2012 KDAB

We want

Qt

C++98

C++11

C++98

Base features Base performance

More features Base performance

C++11

Base features Better performance

More features Better performance

6

© 2013 Intel, 2012 KDAB

App

Solution for Qt’s own code • Enable C++11 automatically • Must still build under C++98 mode • Must provide the same library ABI in either mode • Can use C++11 features with fallback • Can offer new features in .h files (inlines) under #ifdef

7

© 2013 Intel, 2012 KDAB

Compiler support in Qt C++11 support

Minimum version

GCC

• Automatically enabled

4.4 (except on Mac)

Clang

• Automatically enabled if using libc++

Apple Clang: 4.0

‒ Default as of Qt 5.1

ICC Visual Studio 8

© 2013 Intel, 2012 KDAB

Official: 3.0

• Automatically enabled

12.0

• Cannot be disabled

2008

It has not been without problems • Compiler bugs • Different implementations • Implementations of earlier papers / draft standard • Difficulty in making the changes

9

© 2013 Intel, 2012 KDAB

Some C++11 features can be used under #ifdef • Macros for #ifdef: Q_COMPILER_xxx Q_COMPILER_CONSTEXPR, Q_COMPILER_RVALUE_REFS, Q_COMPILER_VARIADIC_TEMPLATES, etc.

• All “interesting” C++11 features listed and checked #ifdef Q_COMPILER_RVALUE_REFS inline QList(QList &&other) : d(other.d) { other.d = const_cast(&QListData::shared_null); } inline QList &operator=(QList &&other) { qSwap(d, other.d); return *this; } #endif #ifdef Q_COMPILER_INITIALIZER_LISTS inline QList(std::initializer_list args) : d(const_cast(&QListData::shared_null)) { qCopy(args.begin(), args.end(), std::back_inserter(*this)); } #endif

10

© 2013 Intel, 2012 KDAB

Some C++11 features don’t require #ifdef • #ifdef is too ugly ‒ ‒ ‒ ‒ ‒ ‒

Q_DECL_EQ_DELETE Q_DECL_EQ_DEFAULT Q_DECL_CONSTEXPR Q_DECL_NOEXCEPT Q_DECL_NOEXCEPT_EXPR(x) Q_NULLPTR No #ifdef Q_DECL_CONSTEXPR inline QFlags(Enum f) : i(f) {} Q_DECL_CONSTEXPR inline QFlags(Zero = 0) : i(0) {} Q_DECL_CONSTEXPR inline QFlags(QFlag f) : i(f) {} template inline uint qHash(const T &t, uint seed) Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(t))) { return (qHash(t) ^ seed); }

11

© 2013 Intel, 2012 KDAB

Some C++11 features are also enabled in C++98 • Macros for C++98 extensions by some compilers ‒ Q_ALIGNOF ‒ Q_DECL_OVERRIDE ‒ Q_DECL_FINAL

GCC’s __alignof__, MSVC’s __alignof MSVC’s override MSVC’s sealed

• Or equivalent behaviour ‒ Q_DECL_NOTHROW ‒ Q_DISABLE_COPY ‒ Q_STATIC_ASSERT

MSVC’s nothrow(), not GCC’s declare copy constructor and assignment op uses sizeof(QStaticAssertFailure)

Q_CORE_EXPORT uint qHash(const QByteArray &key, uint seed = 0) Q_DECL_NOTHROW; Q_CORE_EXPORT uint qHash(const QString &key, uint seed = 0) Q_DECL_NOTHROW;

12

© 2013 Intel, 2012 KDAB

Some features are almost never used • Language syntax features that don’t add performance ‒ Adding #ifdef would reduce readability

• Examples: ‒ ‒ ‒ ‒ ‒ ‒ ‒ ‒ ‒ ‒

Angle bracket for templates without space (>> vs > >) Auto types Class enum Delegating constructors Initialisation of non-static members in the class body Lambdas* New function declaration syntax Range for Raw strings & Unicode strings* Thread-safe initialisation of function statics*

13

© 2013 Intel, 2012 KDAB

* not directly

Close to no use of Standard Library features Standard Library features

Core language features

• Features coming too slowly to the Standard Library

• Features that cannot be implemented without compiler help:

• No reasonable way of detecting them



• We end up duplicating, with Qt • Trouble for: API (e.g. QSharedPointer, ‒ Clang with GCC’s headers QEnableIf) (Mac OS X) ‒ GCC with Dinkumware headers (QNX)

14

© 2013 Intel, 2012 KDAB

The past

15

© 2013 Intel, 2012 KDAB

Qt and C++98 • C++98 support took a long time • MS Visual Studio 6 support dropped only with Qt 4.6 (Dec/2009) ‒ ‒ ‒ ‒

QT_NO_MEMBER_TEMPLATES QT_NO_PARTIAL_TEMPLATE_SPECIALIZATION QT_NO_TEMPLATE_TEMPLATE_PARAMETERS Q_TYPENAME (no typename support)

• Standard Library is a requirement only with Qt 5.0 (Dec/2012) • Qt 5 now requires all C++98 features

16

© 2013 Intel, 2012 KDAB

All C++98 features? No, one remaining... Q_NO_TEMPLATE_FRIENDS

#if defined(Q_NO_TEMPLATE_FRIENDS) public: #else template friend class QSharedPointer; template friend class QWeakPointer; #endif inline void ref() const { d->weakref.ref(); d->strongref.ref(); }

17

© 2013 Intel, 2012 KDAB

The present

18

© 2013 Intel, 2012 KDAB

Data alignment (Q_COMPILER_ALIGNOF, Q_COMPILER_ALIGNAS) • Macro: Q_ALIGNOF ‒ Always present (no #ifdef)

• Macro: Q_DECL_ALIGNED ‒ Not always present!

• Most compilers support alignof • Difficult to emulate ‒ Could be done with an unrestricted as an extension to C++98 ‒ MSVC, GCC, Clang, ICC, IBM xlC, Sun CC ‒ Don’t need to wait for C++11!

• Emulation for older / exotic compilers

19

© 2013 Intel, 2012 KDAB

union

• No good solution

Atomics (Q_COMPILER_ATOMICS) • Qt has had an atomics API since 4.4 (2008) ‒ Has used them since 4.0 (2005)

• Most of it is written in assembly • Only GCC 4.8 generates decent code for atomics ‒ less-than-full memory barriers, no unnecessary locks ‒ GCC 4.7 has support, but it’s reasonable only on x86 / x86-64

We need to keep our assembly for the time being

20

© 2013 Intel, 2012 KDAB

C++11 data races • C++11 finally has a memory model supporting threads • Compiler can be more aggressive when std::atomic is not in use • volatile for threading was wrong! • Qt atomic classes are abusing the compiler

We need to move to std::atomic ASAP; Latent bugs might show up

21

© 2013 Intel, 2012 KDAB

Example of data races • Used to be volatile variables in the Qt event loop @@ -266,8 +266,8 @@ void QEventLoop::exit(int returnCode) if (!d->threadData->eventDispatcher.load()) return; + +

d->returnCode = returnCode; d->exit = true; d->returnCode.store(returnCode); d->exit.storeRelease(true); d->threadData->eventDispatcher.load()->interrupt(); }

@@ -281,7 +281,7 @@ void QEventLoop::exit(int returnCode) bool QEventLoop::isRunning() const { Q_D(const QEventLoop); return !d->exit; + return !d->exit.loadAcquire(); }

22

© 2013 Intel, 2012 KDAB

Future of the Qt atomics • Qt 5.0 saw an overhaul of the code, to simplify ‒ Uses CRTP to provide “virtual” methods without virtual tables

• Missing features: ‒ Compare-and-swap that returns the current value testAndSet + fetchAndStore = fetchAndTestAndSet ? ‒ volatile members ‒ Maybe: implicit load, store and operator overloads, like std::atomic T loadAcquire() const Q_DECL_NOTHROW { return Ops::loadAcquire(_q_value); } void storeRelease(T newValue) Q_DECL_NOTHROW { Ops::storeRelease(_q_value, newValue); } operator T() const Q_DECL_NOTHROW { return loadAcquire(); } T operator=(T newValue) Q_DECL_NOTHROW { storeRelease(newValue); return newValue; }

23

© 2013 Intel, 2012 KDAB

constexpr support (Q_COMPILER_CONSTEXPR) • We added Q_DECL_CONSTEXPR almost everywhere • GCC and Clang did not implement full spec ‒ Code broke with stricter, newer Clang

• Found compiler bugs...

We needed to go back and remove some constexpr

24

© 2013 Intel, 2012 KDAB

constexpr and static initialisation • No load-time overhead ‒ Objects can be static-initialised if they have a constexpr constructor (3.6.2 [basic.start.init] p2)

• Only used for QBasicAtomicInt and QBasicAtomicPointer ‒ and QBasicMutex, but shhhh...

• For all other types, the recommendation is to avoid statics

26

© 2013 Intel, 2012 KDAB

Initialiser lists (Q_COMPILER_INITIALIZER_LISTS) • Feature is provided in the Qt containers • But never used by Qt itself... • And it requires a header to be present

Feature is for the users, not for the library itself...

28

© 2013 Intel, 2012 KDAB

Brace initialisation • Language syntactic sugar in most cases... • Except where it allows us to do something new ‒ Like a constexpr constructor for a class containing an array

#if defined(Q_COMPILER_INITIALIZER_LISTS) && !defined(Q_QDOC) Q_DECL_CONSTEXPR QUuid() : data1(0), data2(0), data3(0), data4{0,0,0,0,0,0,0,0} {} Q_DECL_CONSTEXPR QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3, uchar b4, uchar b5, uchar b6, uchar b7, uchar b8) : data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {} #else

29

© 2013 Intel, 2012 KDAB

Lambdas (Q_COMPILER_LAMBDA) • Support for use of lambdas added to: ‒ QObject::connect ‒ QtConcurrent (requires decltype and the new function syntax)

• Need to add to other slot-type functions • No lambda use in Qt itself...

Feature is for the users, not for the library itself...

30

© 2013 Intel, 2012 KDAB

noexcept support (Q_COMPILER_NOEXCEPT) • Improves code generation of callers! • MSVC’s nothrow() has the semantic of noexcept ‒ But not GCC’s! It implements the C++98 standard

Added it where it made sense, but wait...

31

© 2013 Intel, 2012 KDAB

Does C code throw? • The C language has no support for exceptions... • Unless you’re called Microsoft: ‒ Windows has exceptions in C mode ‒ In fact, crashes are thrown as exceptions!

• Unless you’re using Linux: ‒ POSIX asynchronous cancellations are implemented with exceptions ‒ Possible C++1y feature

32

© 2013 Intel, 2012 KDAB

noexcept macros • Helper macros: ‒ Q_DECL_NOTHROW

‒ Q_DECL_NOEXCEPT ‒ Q_DECL_NOEXCEPT_EXPR(x)

noexcept if supported, nothrow() on MSVC, empty otherwise really noexcept if supported, empty otherwise for use in noexcept expressions

Q_CORE_EXPORT uint qHash(const QByteArray &key, uint seed = 0) Q_DECL_NOTHROW; template inline uint qHash(const T &t, uint seed) Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(t))) { return (qHash(t) ^ seed); }

33

© 2013 Intel, 2012 KDAB

Move constructors (Q_COMPILER_RVALUE_REFS) • Look deceptively easy • Question: what state is a moved object left in? • Can’t use them if using smart pointers and d-pointer / pimpl ‒ Constructor needs to implement destruction for exceptional case

We can’t provide move constructors everywhere

34

© 2013 Intel, 2012 KDAB

Move constructor + smart d-pointer problem

qshareddata.h: In instantiation of ‘QSharedDataPointer::~QSharedDataPointer() [with T = MyClassPrivate]’: /tmp/test.cpp:6:52: required from here qshareddata.h:87:36: error: invalid use of incomplete type ‘struct MyClassPrivate’

35

© 2013 Intel, 2012 KDAB

Move constructor: state of moved-from object • What can you do with v? MyClass v;

other = std::move(v); // v?

• It must: ‒ Be destructible ‒ Be moved onto (swap implementation by triple-move) ‒ What else?

36

© 2013 Intel, 2012 KDAB

Move semantics • Would like to add support everywhere • Huge amount of work • Need to be careful about behaviour compatibility

Work is progressing slowly

37

© 2013 Intel, 2012 KDAB

Ref qualifiers in member functions (Q_COMPILER_REF_QUALIFIERS) • Still investigating QString time(int hrs, int mins) { return QString("%1:%2").arg(hrs).arg(mins, 2, 10, QChar('0')); }

‒ Can we avoid the temporaries?

• Problems: ‒ http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57064 - FIXED in 4.8.2 ‒ Maintaining binary compatibility

38

© 2013 Intel, 2012 KDAB

Static assertions (Q_COMPILER_STATIC_ASSERT) • Really, really useful • Qt provides a fallback for C++98: ‒ Check happens even in C++98 ‒ But misses error message

Implemented fallback, using everywhere

39

© 2013 Intel, 2012 KDAB

Thread-local storage (Q_COMPILER_THREAD_LOCAL) • Some compilers provide support in C++98 (and C): ‒ MSVC ‒ GCC, ICC, Clang

__declspec(thread) __thread

• Qt provides a fallback (QThreadStorage)

Investigate adding an unconditional macro

40

© 2013 Intel, 2012 KDAB

Thread-safe function statics (macro missing) Q_GLOBAL_STATIC • Two problems solved with one solution: ‒ Thread-safety of function (local-scope) statics ‒ Load-time overhead of global statics

• It uses a function (local-scope) static if it’s thread-safe ‒ All compilers adhering to the IA-64 C++ ABI

• Otherwise, it uses a mutex and a guard variable

41

© 2013 Intel, 2012 KDAB

Q_GLOBAL_STATIC features

Q_GLOBAL_STATIC_WITH_ARGS(MyClass, cachedData, (42)) int data() { if (!cachedData.exists()) { // don't create the static if it doesn't exist yet return 42; } return cachedData->i; } // function possibly called during application shutdown int dangerousData() { MyClass *c = cachedData; return c ? c->i : -1; // c is null if it has been already destroyed // also: return cachedData.isDestroyed() ? -1 : cachedData->i; }

42

© 2013 Intel, 2012 KDAB

Unicode strings (Q_COMPILER_UNICODE_STRINGS) String literals

QStringLiteral

• Very useful and welcome

• Always available:

• But never used directly...

‒ Better with lambdas and UTF-16 string literals ‒ Otherwise, falls back to QString::fromUtf8

• Enforces that all source code must be encoded in UTF-8

43

© 2013 Intel, 2012 KDAB

QStringLiteral goals • Returns a QString • No memory allocation → internal data stored in .rodata auto s = QStringLiteral("Hello");

expands to something like... auto s = []() -> QString { enum { Size = sizeof(u"" "Hello") / 2 - 1 }; static const QStaticStringData literal = { Q_STRINGDATA_HEADER(Size), u"" "Hello" }; return const_cast(&literal.header); }

44

© 2013 Intel, 2012 KDAB

The standard committee stopped short... • I wrote this on Linux: u16string s = u"Résumé"; cout << hex << s.at(1) << endl;

How do I print the string?

• It printed:

• If I copy the file to Windows and compile with MSVC¹, what will it print?

45

© 2013 Intel, 2012 KDAB

¹ once it supports Unicode strings

Let’s try...

46

© 2013 Intel, 2012 KDAB

User-defined literals (Q_COMPILER_UDL) • Neat, but we haven’t found use in Qt yet • Will be better in C++1y (see N3599) template QString operator "" _q() { static const QStaticStringData literal = { Q_STRINGDATA_HEADER(sizeof...(c)), { c... } // UTF-16 string } return &literal.header; }

‒ “indeed [...] this form of literal operator has been requested more frequently than any of the forms which C++11 permits” - N3599

47

© 2013 Intel, 2012 KDAB

N3599: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3599.html

Latent bugs • Some code is almost never compiled in C++11 mode ‒ e.g., Windows code, due to MSVC and older GCC versions in MinGW

• Errors show up when the user upgrades (or downgrades!)

We need to keep an eye for bug reports

48

© 2013 Intel, 2012 KDAB

The future

49

© 2013 Intel, 2012 KDAB

C++1y auto function with no return type (Q_COMPILER_AUTO_RETURN_TYPE) • Proposed by N3386 • Implemented in GCC 4.8 with -std=c++1y ‒ No way to detect that flag

• Will most likely not use in Qt for a long time

50

© 2013 Intel, 2012 KDAB

Future directions Finish what we started • Move semantics • Template export control • Standard Library feature detection • “Play” with compiler features

51

© 2013 Intel, 2012 KDAB

What we’d like to see in the language • Complaints from previous slides • Very little in terms of language ‒ C++11 was very good ‒ Probably things we don’t know we need

• Concepts & more meta-programming • Modules • Reflection – get rid of moc

52

© 2013 Intel, 2012 KDAB

How about the Standard Library? • We don’t use much of the library • But we’ll keep an eye out and contribute experience ‒ e.g., std::networking::uri (N3420, N3484, N3507, N3625) ‒ Event loop

• Would like to see simplification of common use-cases ‒ Converting int to std::string / std::u16string ‒ Dealing with user’s locale codec

53

© 2013 Intel, 2012 KDAB

We really need more from compilers and OSes • realloc_inplace (N3495) • futex (Linux) or WaitOnAddress (Windows 8) • Support for SIMD with intrinsics • Support for targeting multiple processor architectures • Tooling like valgrind, helgrind, perf • Something between all-or-nothing debugging symbols • Tighter control over binary compatibility

54

© 2013 Intel, 2012 KDAB

Conclusion

55

© 2013 Intel, 2012 KDAB

Conclusions (1/2) What most developers want: • Put old version into maintenance mode • Require C++11 for newer versions

If you can't afford that: • Target both C++98 and C++11 simultaneously

In any case: • Familiarise yourself with the C++11 memory model

56

© 2013 Intel, 2012 KDAB

Conclusions (2/2) When targeting C++11 & C++98: • Determine which minimum compiler versions you require • Focus on features that require no client code changes • Hide differences in macros • Try to resist NIH, re-use Qt or Boost config macros • Try to keep BC between C++11 and C++98 builds

57

© 2013 Intel, 2012 KDAB

Questions?

Thiago Macieira [email protected]

Links: Website: http://qt-project.org Mailing lists: http://lists.qt-project.org IRC: #qt and #qt-labs on Freenode

58

© 2013 Intel, 2012 KDAB

C++98 features? - GitHub

Software Architect at Intel's Open Source Technology. Center (OTC). • Maintainer of two modules in ... Apple Clang: 4.0. Official: 3.0. 12.0. 2008. C++11 support.

473KB Sizes 13 Downloads 303 Views

Recommend Documents

BUSMASTER © - FlexRay Features - GitHub
Channel Configuration. ▫ Controller Configuration. ▫ Transmission of Messages. ▫ Monitoring Messages. ▫ Network Statistics. ▫ Filters. ▫ Logging. ▫ Signal watch.

New Features Maintenance and Bug Fixes - GitHub
master fix_... maintenance_1.0.x feature_… fix_... Bugfix Release. Tag 1.0.1. Bugfix Release. Tag 1.0.2. Feature Release. Tag 1.1.0 maintenance_1.1.x.

BUSMASTER © - CAN FD Features - GitHub
Option to select CAN FD variable data rate. ❖ Hardware channel ... Signal Watch. Analyze selected signal's Raw and Physical values. ❖ Signal Graph.

Search features
Search Features: A collection of “shortcuts” that get you to the answer quickly. Page 2. Search Features. [ capital of Mongolia ]. [ weather Knoxville, TN ]. [ weather 90712 ]. [ time in Singapore ]. [ Hawaiian Airlines 24 ]. To get the master li

Program features - MCShield
Feb 26, 2012 - Hard disk drives – enables initial scan of all hard drives ..... C:\Documents and Settings\All Users\Application Data\MCShield (for Windows XP).

iAgro Geotag Features -
available on Mobile App and Web Portal. ✓ Geo-Tagging of Land parcels of Commercial Crops. ✓ Periodical field data capturing along with Images & Videos.

Features & Benefits
Web site that offers job seekers more than just job ... of technology businesses that offer Internet marketing,Web site design and hosting, .... It's absolutely free!

Features Scoring Guide
money, percents, time, commas, etc….) Short grafs; quotes stand alone. Has few errors in AP style. (one or two in most stories); or may have non-journalistic paragraph structure. Has several errors in AP style or not in proper journalistic paragrap

Interacting with Features in GPlates
See ​www.earthbyte.org/Resources/earthbyte_gplates.html​ for EarthByte data sets. Background. GPlates ... Feature Type box at the top of the window). .... Indonesian Gateway and associated back-arc basins, Earth-Sci. Rev., vol 83, p.

Linguistic Features of Writing Quality
Writing well is a significant challenge for students and of critical importance for success ... the best predictors of success in course work during their freshmen year of college (Geiser &. Studley ... computationally analyzing essays written by fre

text features sort.pdf
There was a problem previewing this document. Retrying... Download. Connect more apps... Try one of the apps below to open or edit this item. text features ...

Interacting with Features in GPlates
... the new changes. See the GPlates online manual for further information: ... In this exercise we will learn how to query features. To start with we .... 85-138, 1995. Gaina, C., and R. D. Müller, Cenozoic tectonic and depth/age evolution of the.

text features sort.pdf
on a word. Shows events in the. order they occurred. Shows the parts of the. book and the page. numbers. A picture that has been. drawn/painted that. creates a visual. Pages at the back of a. book that tell where to. find important topics. Alphabetic

RingCentral-Features&Pricing.pdf
There was a problem loading more pages. RingCentral-Features&Pricing.pdf. RingCentral-Features&Pricing.pdf. Open. Extract. Open with. Sign In. Main menu.

iAgro Geotag Features -
iAgro Geotag is a Complete Crop Management. Solution for all ... Periodical field data capturing along with Images & Videos ... Field Staff Movement Tracking.

Linguistic Features of Writing Quality
Psychology/Institute for Intelligent Systems ... Phone: (901) 678-3803. E-mail: ...... Washington, DC: U.S. Office of ... Psychological Review, 99, 122-149. Kellogg ...

GitHub
domain = meq.domain(10,20,0,10); cells = meq.cells(domain,num_freq=200, num_time=100); ...... This is now contaminator-free. – Observe the ghosts. Optional ...

GitHub
data can only be “corrected” for a single point on the sky. ... sufficient to predict it at the phase center (shifting ... errors (well this is actually good news, isn't it?)

non fiction features quiz.pdf
Saya telah mengirimkan komplain ke Wa- hana melalui situs, email, whatsapp ke customer service pusat,. namun tidak ada solusi. Mohon tanggapan Wahana ...

Physical Features of Africa Cloze.pdf
... “Sahel” means “______” or “______”. It is a dry, semi-arid region that is slowly turning into___________. People have tried to live here but generations of ...

Features of Campus Commune -
Campus Commune is professional network by TATA Consultancy Services (TCS) that provides a platform to academia and TCS to connect, communicate and ...

text features anchor chart printables.pdf
Page 2 of 9. Drawings. Captions. Bold Print. Page 2 of 9. Page 3 of 9. Glossary. Index. Diagram. Page 3 of 9. Page 4 of 9. Table of Contents. Map. Label. Page 4 ...

Features-of-Magento-Market-Place.pdf
Features-of-Magento-Market-Place.pdf. Features-of-Magento-Market-Place.pdf. Open. Extract. Open with. Sign In. Main menu. Displaying ...