Code Monkey home page Code Monkey logo

qtprotobuf's Introduction

QtProtobuf and QtGrpc are ported to Qt6 and now are part of the Qt project.

QtProtobuf

gRPC and Protobuf generator and bindings for Qt framework

see Protobuf and gRPC for more information

QtProtobuf provides Qt-native support of Google protocol buffers. Generated code doesn't depend on any framework except Qt framework. QtProtobuf uses existing Qt meta-object information to access object fields and supports most of features of original C++ protocol buffers. It's not just a wrapper arround protocol buffers, but pure Qt generator with own serializers. Our target to provide deep Qt, protocol buffers and gRPC intergration.

Test results

Branch Results Support
master ✔️
0.5 ✔️
0.4 ✔️
0.3 ✔️
0.2
0.1.0

Table of contents

API Reference

Linux Build

Windows Build

Cross-compiling

Usage

Integration with CMake project

Integration with qmake project

Generated and QtProtobuf types registation

Code exceptions

QtProtobuf development

Tutorials

QtProtobuf Client Tutorial

Build

Build options

QT_PROTOBUF_MAKE_COVERAGE - if TRUE/ON, enables gcov intergration, to collect code coverage reports(usefull for developers). FALSE by default.

QT_PROTOBUF_MAKE_TESTS - if TRUE/ON, enables tests for QtProtobuf. TRUE by default.

QT_PROTOBUF_MAKE_EXAMPLES - if TRUE/ON, enables built-in examples. TRUE by default.

QT_PROTOBUF_NATIVE_GRPC_CHANNEL - if TRUE/ON, enables build of an additional channel wrapping native gGRPC C++ library (Note: grpc++ library is required).

BUILD_SHARED_LIBS - if TRUE/ON, enables shared libraries build, FALSE by default, static libraries build is performed.

Note: In case if you use static QtProtobuf in your non-cmake/-qmake build system, you additionaly need manually add QT_PROTOBUF_STATIC compiler definition.

QT_PROTOBUF_FIELD_ENUM - if TRUE/ON, adds enumeration with message fields for generated messages in QtProtobufTypes and QtProtobufWellKnownTypes libraries. FALSE by default.

Linux Build

Prerequesties

Check installation of following packages in your system:

  • cmake 3.14 or higher
  • Qt 5.12.4 or higher
  • protobuf 3.6.0 or higher (might be used from submodule)
  • golang 1.10 or higher

Optional:

  • grpc 1.15.0 or higher (might be used from submodule)

Note: Older versions could be supported as well but not tested.

Option 1: Use system libraries

For Ubuntu 19.10 or higher

Install dependencies:

sudo apt-get
    build-essential \
    cmake \
    golang \
    wget \
    libdbus-1-3 \
    libfreetype6 libfontconfig libx11-6 \
    libgl1-mesa-dev \
    libsm6 \
    libice6 \
    libxext6 \
    libxrender1 \
    doxygen \
    qt5-default \
    qtdeclarative5-private-dev \
    qtbase5-private-dev \
    protobuf-compiler \
    libprotoc-dev \
    protobuf-compiler-grpc \
    libgrpc++-dev \
    libgrpc-dev \
    libgtest-dev
For OpenSUSE 15.2 or higher

Install dependencies:

sudo zypper in go \
    wget \
    cmake \
    libXrender1 \
    libXext6 \
    libSM6 \
    Mesa-libGL1 \
    libfreetype6 \
    libX11-6 \
    libdbus-1-3 \
    fontconfig \
    libICE6 \
    gcc-c++ \
    Mesa-devel \
    libgthread-2_0-0 \
    libqt5-qttools \
    protobuf-devel \
    grpc-devel \
    libqt5-qtdeclarative-devel \
    libqt5-qtbase-devel \
    libqt5-qtdeclarative-private-headers-devel \
    libqt5-qtbase-private-headers-devel \
    rpm-build
sudo zypper --non-interactive in --type pattern devel_C_C++

Option 2: All-in-one build

If required versions of libraries are not found in your system, you may use all-in-one build procedure for prerequesties.

Manually install Qt version you need .

Update submodules to fetch 3rdparty dependencies:

git submodule update --init --recursive

Note: All installation rules are disabled for all-in-one build.

Option 3: Use as sub-module

QtProtobuf build procedure is designed to use QtProtobuf as sub-project.

Note: This option only supported by projects that use CMake as build tool.

Add QtProtobuf as git submodule in your Project folder:

git submodule add https://github.com/semlanik/qtprotobuf.git qtprotobuf
git submodule init qtprotobuf
git submodule update qtprotobuf

#(Optional) You also may initialize all internal QtProtobuf sub-modules for all-in-one build using steps below:
cd qtprotobuf
git submodule update --init --recursive

Next add QtProtobuf subproject to your CMake tree, edit CMakeLists.txt:

...
add_subdirectory("qtprotobuf")
...

QtProtobuf and dependencies from source tree will be built within your Project.

Note: All installation rules of QtProtobuf project are disabled for sub-project build.

Build

git submodule init src/protobuf/3rdparty/microjson #microjson is part of build and installation tree. Made this to simplify dependency management, but you always may use system microjson if installed.
git submodule update src/protobuf/3rdparty/microjson
mkdir build
cd build
cmake .. [-DCMAKE_PREFIX_PATH="<path/to/qt/installation>/Qt<qt_version>/<qt_version>/gcc_64/lib/cmake"]
cmake --build . [--config <RELEASE|DEBUG>] -- -j<N>

Packaging

QtProtobuf has packaging support based on CPack.

.deb

You can create .deb package for debian-like operating systems, using commands below:

mkdir build
cd build
cmake .. -DQT_PROTOBUF_MAKE_TESTS=FALSE -DQT_PROTOBUF_MAKE_EXAMPLES=FALSE
cpack -G DEB ..

Note: Only tested on Ubuntu 19.10

.rpm

You can create .rpm package for rpm-based operating systems, using commands below:

mkdir build
cd build
cmake .. -DQT_PROTOBUF_MAKE_TESTS=FALSE -DQT_PROTOBUF_MAKE_EXAMPLES=FALSE
cpack -G RPM ..

Note: Only tested on OpenSUSE 15.2

Windows Build

Prerequesties

Download and install:

  • Qt 5.12.3 or higher 1
  • cmake-3.6 or higher 2
  • Strawberry perl 5.28 3
  • GoLang 1.10 or higher 4
  • Yasm 1.3 or higher 5
  • Actual Visual Studio Compiler with cmake support that required by Qt 6

Note: All applications should be in PATH

Update submodules to fetch 3rdparty dependencies:

git submodule update --init --recursive

Build

Open Qt MSVC command line and follow steps:

"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" <x86|x64>
cd <directory with qtprotobuf project>
mkdir build
cd build
cmake ..
cmake --build . [--config <RELEASE|DEBUG>] -- /m:<N>

Cross-compiling

Note: Cross-compiling support is available since version 0.6.0

Cross compiling is only supported in the prefixed builds of QtProtobuf. It's split into three parts:

  • Build and install the Qt framework for the target system (Instructions could be found in the documentation for the Qt Framework)
  • Build for the host system
  • Build for the target system

Build for the host system

Build for the host system must be prefixed, that means you should install it to the system or the temporary directory, using the following steps:

mkdir build-host
cd build-host
cmake </path/to/qtprotobuf/sources> -DQT_PROTOBUF_MAKE_TESTS=OFF -DQT_PROTOBUF_MAKE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=</host/sysroot/prefix>
cmake --build . -- -j<N>
cmake --install .

Build for the target system

To build QtProtobuf correctly for the target system you need to specify compiler and other build essentials in the CMake toolchain file. Also it's necessary to specify the QT_PROTOBUF_HOST_PATH variable, that is used to find a host binary of the QtProtobuf generator.

⚠️ IMPORTANT The QT_PROTOBUF_HOST_PATH variable shall point to the <lib|lib32|lib64>/cmake directory of the host QtProtobuf installation.

Typical toolchain file looks as following:

set(CMAKE_SYSTEM_NAME Linux)

set(CMAKE_SYSROOT /usr/armv7a-hardfloat-linux-gnueabi/)

set(CMAKE_C_COMPILER /usr/bin/armv7a-hardfloat-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/armv7a-hardfloat-linux-gnueabi-g++)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # Important to look programms in the host sysroot only
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) # Important to look packages in both the host and target sysroots

Using the below steps configure and build the target QtProtobuf:

Note: Use values of the expressions same as in the Build for the host system step.

mkdir build-host
cd build-host
cmake </path/to/qtprotobuf/sources> -DQT_PROTOBUF_MAKE_TESTS=OFF -DQT_PROTOBUF_MAKE_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX="</target/sysroot/prefix>" -DCMAKE_TOOLCHAIN_FILE="</path/to/toolchain.cmake>" -DQT_PROTOBUF_HOST_PATH="</host/sysroot/prefix>/<lib|lib32|lib64>/cmake" -DCMAKE_PREFIX_PATH="</target/sysroot/cross-compiled/qt>"

Build your project using the target QtProtobuf

Note: Only the CMake-based user projects have a cross-compiling support.

When you build your own project using the target QtProtobuf, it's recommented to use the same toolchain file as you used to compile QtProtobuf. You also still need to specify the QT_PROTOBUF_HOST_PATH variable to find the host QtProtobuf generator. Use the following steps as example:

mkdir build-clienttutorial
cd build-clienttutorial
cmake </path/to/qtprotobuf/sources/examples/clienttutorial> -DCMAKE_PREFIX_PATH="</target/sysroot/prefix>;</target/sysroot/cross-compiled/qt>" -DQT_PROTOBUF_HOST_PATH="</host/sysroot/prefix>/<lib|lib32|lib64>/cmake" -DCMAKE_TOOLCHAIN_FILE="</path/to/toolchain.cmake>"

If you build the shared version of the QtProtobuf libraries don't forget to copy them to the target filesystem before running the application.

Conan build

Note: QtProtobuf supports conan builds since version 0.4.0.

Build QtProtobuf

Before build make sure that all sub-modules are deinitilized:

git submodule deinit -f --all

Build QtProtobuf:

mkdir -p build_conan
cd build_conan
conan source ..
conan install .. --build=microjson
conan build ..

Conan project integration

QtProtobuf conan source packages are hosted on JFrog Bintray. Add remote to conan:

conan remote add semlanik https://api.bintray.com/conan/semlanik/libs

To intergrate QtProtobuf add it as a dependency to your conanfile.py:

    ...
    requires = [
        ...
        "qtprotobuf/0.5.0@semlanik/stable",
        ...
    ]
    ...

Note: You also may use QtProtobuf package is built localy or from other remote source. Look in conan documentation for details.

Usage

Direct usage of generator

[QT_PROTOBUF_OPTIONS="[SINGLE|MULTI]:QML:COMMENTS:FOLDER:FIELDENUM:EXTRA_NAMESPACE=<value>"] protoc --plugin=protoc-gen-qtprotobuf=<path/to/bin/>qtprotobufgen --qtprotobuf_out=<output_dir> [-I/extra/proto/include/path] <protofile>.proto

QT_PROTOBUF_OPTIONS

For protoc command you also may specify extra options using QT_PROTOBUF_OPTIONS environment variable and colon-separated format:

[QT_PROTOBUF_OPTIONS="[SINGLE|MULTI]:QML:COMMENTS:FOLDER:FIELDENUM:EXTRA_NAMESPACE=<value>"] protoc --plugin=protoc-gen-qtprotobuf=<path/to/bin/>qtprotobufgen --qtprotobuf_out=<output_dir> [-I/extra/proto/include/path] <protofile>.proto

Following options are supported:

SINGLE - enables single-file generation when for each .proto file single pair of .h .cpp files is generated

Note: Enabled by default. Excluded by SINGLE

MULTI - enables multi-file generation when for each message separate pair of .h .cpp

Note: Has higher priority than SINGLE

QML - enables QML code generation in protobuf classes. If is set QML-related code for lists and QML registration to be generated.

COMMENTS - enables comments copying from .proto files

FOLDER - enables folder-based generation

FIELDENUM - adds enumeration with message fields for generated messages.

Integration with CMake project

You can integrate QtProtobuf as submodule in your project or as installed in system package. Add following line in your project CMakeLists.txt:

...
find_package(QtProtobuf CONFIG REQUIRED COMPONENTS ProtobufGenerator Protobuf Grpc)
file(GLOB PROTO_FILES ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}/path/to/protofile1.proto
 ${CMAKE_CURRENT_SOURCE_DIR}/path/to/protofile2.proto
 ...
 ${CMAKE_CURRENT_SOURCE_DIR}/path/to/protofileN.proto)
# Function below generates source files for specified PROTO_FILES,
# and link them to the MyTarget as static library
add_executable(MyTarget main.cpp) # Add your target here
qtprotobuf_generate(TARGET MyTarget
OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated
PROTO_FILES ${PROTO_FILES})

Another option is to pre-specify expected generated headers to prevent dummy-parser mistakes

...
qtprotobuf_generate(TARGET MyTarget
OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/generated
PROTO_FILES ${PROTO_FILES})
...

In case if you somehow avoided qtprotobuf_generate usage, you need manualy link QtProtobuf libraries used by project, e.g.:

...
target_link_libraries(${TARGET} QtProtobuf::Protobuf QtProtobuf::Grpc QtProtobuf::ProtobufWellKnownTypes)
...

CMake functions reference

qtprotobuf_generate

qtprotobuf_generate is cmake helper function that automatically generates STATIC library target from your .proto files

Parameters:

TARGET - a name of your target that the generated code will be linked to.

GENERATED_TARGET - name that will be used for generated archive library target. It's usefull when you supposed to have multiple generated targets to be linked to single one.

OUTPUT_DIRECTORY - output directory that will contain generated artifacts. Usually subfolder in build directory should be used

EXCLUDE_HEADERS - List of header files to be excluded from pre-parsed list of expected header files (e.g. nested messages that are not supported by QtProtobuf generator)

PROTO_FILES - List of .proto files that will be used in generation procedure

Options:

MULTI - Enables multi-files generation mode. If provided in parameter list generator will create pair of header/source files for each message.

Note: multi-files generation mode is defined as deprecated by QtProtobuf team, and might have poor support in future

QML - Enables QML code generation in protobuf classes. If provided in parameter list QML related code for lists and QML registration to be generated.

COMMENTS - Enables comments copying from .proto files. If provided in parameter list message and field related comments will be copied to generated header files.

FOLDER - Enables folder based generation. If provided in parameter list generator will place generated artifacts to folder structure according to package of corresponding .proto file.

Note: enabled by default if MULTI option provided

FIELDENUM - Adds enumeration with message fields for generated messages.

EXTRA_NAMESPACE - Wraps the generated code with the specified namespace. (EXPERIMETAL)

qtprotobuf_link_target

qtprotobuf_link_target is cmake helper function that links generated protobuf target to your binary. It's useful when you try to link generated target to shared library or/and to executable that doesn't utilize all protobuf generated classes directly from C++ code, but requires them from QML.

Parameters:

TARGET - name of target to link to

GENERATED_TARGET - protobuf generated target name

Useful definitions

QT_PROTOBUF_EXECUTABLE - contains full path to QtProtobuf generator

Integration with qmake project

Note: Available in QtProtobuf version >=0.2.0

QtProtobuf has limited qmake build procedures support. It's only available and tested on linux platforms. To use it in your qmake project, first you need build and install QtProtobuf as standalone project in your system paths:

mkdir build
cd build
cmake .. [-DCMAKE_PREFIX_PATH="<path/to/qt/installation>/Qt<qt_version>/<qt_version>/gcc_64/lib/cmake"] -DCMAKE_INSTALL_PREFIX=/usr -DQT_PROTOBUF_MAKE_TESTS=OFF -DQT_PROTOBUF_MAKE_EXAMPLES=OFF
cmake --build . [--config <RELEASE|DEBUG>] -- -j<N>
sudo cmake --build . [--config <RELEASE|DEBUG>] --target install

Commands above will install qt protobuf into you system paths prefixed with /usr folder.

Once build and installation is finished, you may use QtProtobuf and QtGrpc in qmake project by adding following lines in .pro file:

QT += protobuf #for protobuf libraries support
QT += grpc #for grpc libraries support

To generate source code and link it to you project use predefined qtprotobuf_generate function

qmake functions reference

qtprotobuf_generate([generate_qml=false])

qtprotobuf_generate is qmake helper test function that generates QtProtobuf source code based on files provided by PROTO_FILES global context variable

Parameters:

generate_qml - Enables/disables QML code generation in protobuf classes. If set to true QML-related code for lists and QML registration to be generated.

Note: see examples/qmakeexample for details

Generated and QtProtobuf types registation

To enable QtProtobuf project you need to register protobuf types. It's good practice to make it in 'main' function.

...
#include <QtProtobufTypes>
...
int main(int argc, char *argv[])
{
QtProtobuf::qRegisterProtobufTypes();
... //Qt application initialization and run
}

Code exceptions

If any prohibited Qt/QML keyword is used as field name, generator appends '_proto' suffix to generated filed name. It's required to omit overloading for example QML reserved names like 'id' or 'objectName'.

E.g. for message:

message MessageReserved {
    sint32 import = 1;
    sint32 property = 2;
    sint32 id = 3;
}

Following properties will be generated:

...
    Q_PROPERTY(QtProtobuf::sint32 import_proto READ import_proto WRITE setImport_proto NOTIFY import_protoChanged SCRIPTABLE true)
    Q_PROPERTY(QtProtobuf::sint32 property_proto READ property_proto WRITE setProperty_proto NOTIFY property_protoChanged SCRIPTABLE true)
    Q_PROPERTY(QtProtobuf::sint32 id_proto READ id_proto WRITE setId_proto NOTIFY id_protoChanged SCRIPTABLE true)
...

Note: List of exceptions might be extended in future releases.

QtProtobuf development

Running tests

cd <build directory>
ctest

Documentation generation

Project uses doxygen for documentation generation.

Ubuntu

Install doxygen and graphviz packages:

sudo apt-get install -y doxygen graphviz

Windows

Install doxygen and graphviz packages:

Generation

You can generate documentation for actual version using following commands:

mkdir build
cd build
cmake ..
cmake --build . --target doc

qtprotobuf's People

Contributors

gamepad64 avatar gmabey avatar rapgenic avatar semlanik avatar tatianaborisova avatar vifactor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qtprotobuf's Issues

protobuf and grpc versions

How hard are the version requirements on protobuf (3.6.0) and grpc (1.15.0)?
I ask because I'm trying to make qprotobuf work on the NVIDIA Jetson Nano, and there's no good way (that I've found so far) to upgrade it beyond Ubuntu 18.04, which includes protobuf 3.0.0 and grpc 1.3.2 . My attempts to compile them from source have been frustrated -- in the compilation of grpc there's a compile error similar to what's described in facebook/folly#1098 but without the option of exercising that OP's fix.
I've also tried making gcc8 the default -- that didn't help either.
I think that gprotobuf 3.6 is not going to be a problem; I think it's just grpc.

Are there specific grpc-1.15.0 features/fixes that are needed for qtprotobuf?

"transparent" types break "lvalue" and "rvalue" concepts, introducing errors

Using "transparent" type to define fields of output C++ Qt classes introduces bugs and potential pitfalls - since it allows a value to be assigned to a getter function call. Here is some code to illustrate:

transparent<int> m;

transparent<int> GetM() { return m; }

int n;

int GetN() { return n; }

int main()
{
    std::cout << "Hello World!\n";

    GetM() = 25; // No warning or compiler error
    GetN() = 26; // error C2106: '=': left operand must be l-value 
}

Assigning a value to the transparent value getter appears legal and does not produce a warning or an error. This leads to logic errors since usage of setters is no longer enforced - and the assignment does not produce an expected result.

For us, this is a big issue. We are trying to replace a bunch of plain C structs with proto-generated C++ Qt classes. Simply replacing struct field references with class getters results in hard-to-detect errors - since assigning a value to a getter is legal.

Problems with "transparent" types

The types using "transparent" wrapper break a lot of QML functionality: To demonstrate, I created this proto:

syntax = "proto3";

package test;

message Test1 {
	int32 count = 1; 
}

The generated class defines the property as follows:

image

I also created another class Test2 manually, using "int" as the property type:

image

I then registered both types with QML:

    // Register Qt Protobuf-generated types
    QtProtobuf::qRegisterProtobufTypes();

    qmlRegisterType<Test2>("test", 1, 0, "Test2");

Problem # 1: Property assignment is broken

    property Test1 test1: Test1 {
        count: 0
    }

    property Test2 test2: Test2 {
        count: 0
    }

The above results in error:

qrc:/main.qml:13 Invalid property assignment: unsupported type "QtProtobuf::int32"

So, the assignment is ok for Test2 but not for Test1.

Problem # 2: Implicit boolean conversion is broken

I comment out the broken assignment so that both test1 and test2 have count = 0 and display the following:

    Row {
        Label {
            text: test1.count ? "TRUE" : "FALSE"
        }

        Label {
            text: "   "
        }

        Label {
            text: test2.count ? "TRUE" : "FALSE"
        }
    }

The result is:

image

It was expected that test1.count (being an integer equal to 0) would convert to "false" - but the opposite is the case. I guess that the implicit conversion in this case is the "null" test instead.

Problem # 3: Locale formatting is broken

The transparent types are not recognized as "Number" by QML. So the following code also results in unexpected output:

    Row {
        Label {
            text: test1.count.toLocaleString(Qt.locale())
        }

        Label {
            text: "   "
        }

        Label {
            text: test2.count.toLocaleString(Qt.locale())
        }
    }

Results in:

image

Is there any way to make "transparent" types compatible with expected QML behavior, or to get rid of these wrappers?

Add QML registration

At the moment Qt Protobuf output provides a convenient way to register Qt types through qRegisterProtobufTypes. However, to make the types available to QML, qmlRegisterType has to be still manually called for each new type.

Could you add QML type registration to qRegisterProtobufTypes as well?

Thanks,

Michael

documentation on using google's well-known types

I'm now trying to include google.protobuf.Timestamp in a message, and I haven't been able to find anything in the documentation regarding special steps that [may] need to be taken in order for this to work. Could you please elaborate a little on how that should be done?

Improve gRPC QML integration

Current gRPC API doesn't fully supported by QML. Need to design and implement additional QML helper classes and make deeper integraion.

Only the last compiled .proto file output is included in "qtprotobuf_global.qpb.h"

It seems that only the class output of only the last compiled .proto file is included in "qtprotobuf_global.qpb.h". For example, if the project contains Test1.proto and Test2.proto, during build I see the following:

Compiling Protobuf schema Test1.proto (Qt)
Compiling Protobuf schema Test2.proto (Qt)

So, protoc is executed twice - once for each file. The output ""qtprotobuf_global.qpb.h" file contains the following (note that Test1 is missing):

/* This file is autogenerated. DO NOT CHANGE. All changes will be lost */

#pragma once

#include
#include
#include
#include
#include

#include
#include <unordered_map>

#include "Test2.qpb.h"

namespace nvram {
inline void qRegisterProtobufTypes() {
qRegisterProtobufType<Test2>();
}
}

Compilation Error

When I compile the project according to your instructions I get the following error

simpletest.cpp:(.text._ZN19qtprotobufnamespace5tests20StepChildEnumMessageD2Ev[_ZN19qtprotobufnamespace5tests20StepChildEnumMessageD5Ev]+0xf): undefined reference to `vtable for qtprotobufnamespace::tests::StepChildEnumMessage'
collect2: error: ld returned 1 exit status
gmake[2]: *** [tests/test_protobuf/CMakeFiles/qtprotobuf_test.dir/build.make:167: tests/test_protobuf/qtprotobuf_test] Fehler 1
gmake[1]: *** [CMakeFiles/Makefile2:583: tests/test_protobuf/CMakeFiles/qtprotobuf_test.dir/all] Fehler 2
gmake: *** [Makefile:141: all] Fehler 2

Do you have an idea what I'm doing wrong?

Thanks for your help

enhancement suggestion for README.md

diff --git a/README.md b/README.md
index ddbae71..f8c4233 100644
--- a/README.md
+++ b/README.md
@@ -84,6 +84,7 @@ Download and install:
 - GoLang 1.10 or higher [4](https://golang.org/dl/)
 - Yasm 1.3 or higher [5](http://yasm.tortall.net/Download.html)
 - Actual Visual Studio Compiler with cmake support that required by Qt [6](https://visualstudio.microsoft.com/downloads/)
+- protobuf 3.6.0 or higher [7](https://github.com/protocolbuffers/protobuf/releases) -- pick the protobuf-cpp-X.Y.Z.zip -- unzip it to a convenient place, then follow the instructions in cmake/README
 
 >**Note:** All applications should be in PATH

Add support for Qt-centric less-well-known types

There is no standard UUID message distributed with protobuf. However, it would be really nice to be able to automatically convert a message member to a QUuid, don't you agree?

This may entail distributing a set of .proto files with qtprotobuf, that, if used as part of the user's .proto-defined messages, would get automatically translated that way.

UPD by @semlanik:
Following types will be added as less-well-known types:

  • QUrl

  • QChar

  • QUuid

  • QLocale - Not unified

  • QColor

  • QDate

  • QTime

  • QDateTime

  • QSize

  • QSizeF

  • QRect

  • QRectF

  • QPolygon

  • QPolygonF

  • QPoint

  • QPointF

  • QImage

  • QPixmap - is internal Qt representation of image. Not sure if applicable to transfer it using protocol buffers

  • QIcon - not something that has own data.

  • QBitmap - inherited of QPixmap

  • QQuaternion

  • QTransform

  • QMatrix - Deprecated by Qt.

  • QMatrix4x4

  • QVector2D

  • QVector3D

  • QVector4D

  • QEasingCurve - skip for now. Will add by request. It seems this field should be of google.protobuf.Any type

Output not following naming convention, breaking VS Custom Build Step

Any other protoc generator that outputs C++/C# code uses name of the source file for the output file name, producing only one output source file for C# and a pair of H/CPP for C++.

For example, "security.proto" defining Account and Login messages would result in "security.pb.h" and "security.pb.cc" output files, containing both Account and Login classes.

qtprotobuf generates a pair of H/CPP for each message and uses class name for naming each pair.

For example, the same "security.proto" from above results in "Account.cpp", "Account.h", "Login.cpp", "Login.h".

Here are the problems with this approach:

  1. It deviates from convention, producing confusion. It is no longer apparent which proto file produces a certain generated file - unless you go into code and search by name.

  2. It breaks custom build step used by Visual Studio, since for proper cleanup/rebuild the step needs to know each output file name - which becomes a tedious manual work when there are multiple messages in a proto file. Adding a new message to existing proto file would involve going into custom build step setting for that proto file and manually adding names of the output files for the new message:

image

For a single pair of "pb.h" and "pb.cc" per proto file, custom build step output files can be generically specified as:
$(InputDir)\$(InputName).pb.cc;$(InputDir)\$(InputName).pb.h

image

crash on multi-threaded initialization

It looks to me that the static data member of QProtobufSerializerPrivate, "SerializerRegistry handlers" needs to be protected by a mutex. My program frequently crashes when two separate threads both instantiate a QProtobufSerializer at the same time. Better yet, have you considered using Q_GLOBAL_STATIC? You might still need a mutex (I would use Q_GLOBAL_STATIC_WITH_ARGS to give it a QMutex::Recursive behavior) depending on whether you decide to change how "handlers" gets populated.

I think that the primary advantage of Q_GLOBAL_STATIC lies in its ability to preserve the uniqueness of "handlers" across shared library (DLL) boundaries.

Simplify Qt protobuf type registration

A while ago, I reported issues with statically registering Qt protobuf types due to "Static Initialization Order Fiasco." In response to that report, you removed automatic registration and replaced it with the "qRegisterProtobufTypes" function in qtprotobuf_global.qpb.h that can be called manually by the developer. The main problem with this approach for Windows developers (who often do not use CMAKE for VS projects) is that only the last processed type shows up in qRegisterProtobufTypes if the .proto files are processed manually with "protoc." So, such developers (including me) are forced to manually add qRegisterProtobufType<...>() call for each new type. Here is the better way which avoids both issues:

  1. Define static namespace to hold type registration function lambdas for the types:

a) QtProtobufRegister.h

namespace QtProtobufRegister
{
	typedef std::function<void(void)> RegisterFunction;

	void Init();
	QList<RegisterFunction>& GetList();

	template <class T>
	struct Type {
		Type(RegisterFunction initializer)
		{
			GetList().append(initializer);
		}
	};
};

b) QtProtobufRegister.cpp

#include "QtProtobufRegister.h"

namespace QtProtobufRegister
{
	QList<RegisterFunction>& GetList()
	{
		static QList<std::function<void(void)>> registrationList;
		return registrationList;
	}

	void Init()
	{
		for (auto registerFunc : GetList())
		{
			registerFunc();
		}
	}
}
  1. For generated code, add the following static variable to the class CPP file (inside namespace but before the class definition):
    static QtProtobufRegister::Type<{CLASS_TYPE}> s_protobufRegister([](){
        qRegisterProtobufType<{CLASS_TYPE}>();
    });
  1. When it is finally safe to register the Qt types, a developer can simply call the following to register all Qt Protobuf types all at once:

QtProtobufRegister::Init();

The way this works is for every class its registration call is not performed immediately when the class is loaded statically - but rather is stored in the list. When 'QtProtobufRegister::Init()' is caller - all registration function from that list are executed, avoiding the Static Initialization fiasco.

I've successfully used a similar approach to register over 100 QML types in our application.

Another advantage is that you can drop "qtprotobuf_global.qpb.h" file as redundant.

"Static Initialization Order Fiasco" with QProtobufRegistrationHelper

The generator produces this "helpful" piece of code in the output:

static QtProtobuf::QProtobufRegistrationHelper helper(registerTypes);

This static variable often leads to crash on startup when running VS-compiled executables, due to well-known "static order initialization fiasco":

https://stackoverflow.com/questions/41938297/calling-qmlregistertype-in-the-registered-class-on-debug-crashes-application

Basically, at the time QProtobufRegistrationHelper tries to register the type, the type's static metadata has not been created yet - causing the crash (since the order is indeterminate).

Suggestion - do not attempt to call registerTypes() statically. Instead, provide a static method and leave the decision up to a developer to execute when appropriate

Better suggestion - store pointer to each registerTypes() in a static list. Provide static function to call all functions on the list at the time of developer's choosing.

Build fails on Windows

The Jenkins build fails on Windows with these lines:

Generating generated/moc_simplefixed32sint32mapmessage.cpp
E:\Jenkins\workspace\QtProtobuf\3rdparty\grpc\third_party\boringssl\tool\transport_common.cc(19): error C2220: warning treated as error - no 'object' file generated [E:\Jenkins\workspace\QtProtobuf\build\3rdparty\grpc\third_party\boringssl\tool\bssl.vcxproj]
E:\Jenkins\workspace\QtProtobuf\3rdparty\grpc\third_party\boringssl\tool\transport_common.cc(19): warning C4005: '_SCL_SECURE_NO_WARNINGS': macro redefinition [E:\Jenkins\workspace\QtProtobuf\build\3rdparty\grpc\third_party\boringssl\tool\bssl.vcxproj]
E:\Jenkins\workspace\QtProtobuf\3rdparty\grpc\third_party\boringssl\tool\transport_common.cc: note: see previous definition of '_SCL_SECURE_NO_WARNINGS'

The build is performed as follows:

  1. Fresh code is checked out of "https://github.com/semlanik/qtprotobuf"
  2. Path is set as "Path=$Path;C:\Qt\5.13.2\msvc2017\bin"
  3. call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\VC\Auxiliary\Build\vcvars32.bat"
  4. git submodule update --init --recursive
  5. mkdir build
  6. mkdir install
  7. cd build
  8. "C:\Program Files\CMake\bin\cmake.exe" .. -DCMAKE_PREFIX_PATH="C:\Qt\5.13.2\msvc2017\lib\cmake" -DCMAKE_INSTALL_PREFIX="..\install" -DCMAKE_DEBUG_POSTFIX=D -G "Visual Studio 15 2017"
  9. "C:\Program Files\CMake\bin\cmake.exe" --build . --config Debug --target INSTALL -- /m:4
  10. "C:\Program Files\CMake\bin\cmake.exe" --build . --config RelWithDebInfo --target INSTALL -- /m:4

Please find the full log below:

log.txt

Invalid Field Naming in Generated Qt Class (different names in H and CPP)

The generator does not handle field names with "_" correctly and generates different names in ".h' and ".cpp" output files - causing compiler failure.

For example, consider this proto:

syntax = "proto3";

package test;

message Test
{
	string some_field = 1;
}

Output contains these lines in ".h":

private:
    QString m_someField;

While ".cpp" contains:

Test::Test(const QString &some_field, QObject *parent) : QObject(parent)
    , m_some_field(some_field)
{
}

Note the difference in field naming (m_someField vs m_some_field).

P.S. It would be great to be able to select the naming convention of the output somehow - so that the output will match VS formatting used by the developer.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.