Zeromq hello world plugin.

develop
Petr Mrázek 2011-11-25 22:14:36 +01:00
parent 57b5ed8fa7
commit 780b659210
11 changed files with 827 additions and 1 deletions

@ -35,7 +35,7 @@ fi
# Now run
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./stonesense/deplibs"
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"./stonesense/deplibs":"./hack/deplibs"
case "$1" in
-g | --gdb)

@ -25,6 +25,11 @@ if(BUILD_DEV_PLUGINS)
add_subdirectory (devel)
endif()
OPTION(BUILD_SERVER "Build server." OFF)
if(BUILD_SERVER)
add_subdirectory (server)
endif()
OPTION(BUILD_DF2MC "Build DF2MC (needs a checkout first)." ON)
if(BUILD_DF2MC)
add_subdirectory (df2mc)

@ -0,0 +1,32 @@
PROJECT (server)
SET(PROJECT_SRCS
main.cpp
)
include_directories( ${CMAKE_SOURCE_DIR} )
IF(UNIX)
OPTION(SERVER_INTERNAL_SO "Link with prebuilt internal zeromq lib and headers." ON)
IF(SERVER_INTERNAL_SO)
SET(PROJECT_LIBS
${server_SOURCE_DIR}/zeromq/libzmq.so.1
${PROJECT_LIBS}
)
include_directories (
${include_directories}
${server_SOURCE_DIR}/zeromq
)
install(PROGRAMS ${server_SOURCE_DIR}/zeromq/libzmq.so.1 DESTINATION "hack/deplibs")
ELSE()
SET(PROJECT_LIBS
zmq
${PROJECT_LIBS}
)
ENDIF()
ELSE()
ENDIF()
DFHACK_PLUGIN(server ${PROJECT_SRCS} LINK_LIBRARIES ${PROJECT_LIBS})
add_executable ( helloclient hello.cpp )
target_link_libraries ( helloclient ${server_SOURCE_DIR}/zeromq/libzmq.so.1)
install(TARGETS helloclient RUNTIME DESTINATION . )

@ -0,0 +1,39 @@
//
// Hello World client
// Connects REQ socket to tcp://localhost:5555
// Sends "Hello" to server, expects "World" back
//
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
int main (void)
{
void *context = zmq_init (1);
// Socket to talk to server
printf ("Connecting to hello world server…\n");
void *requester = zmq_socket (context, ZMQ_REQ);
zmq_connect (requester, "tcp://localhost:5555");
int request_nbr;
for (request_nbr = 0; request_nbr != 10; request_nbr++)
{
zmq_msg_t request;
zmq_msg_init_size (&request, 5);
memcpy (zmq_msg_data (&request), "Hello", 5);
printf ("Sending Hello %d…\n", request_nbr);
zmq_send (requester, &request, 0);
zmq_msg_close (&request);
zmq_msg_t reply;
zmq_msg_init (&reply);
zmq_recv (requester, &reply, 0);
printf ("Received World %d\n", request_nbr);
zmq_msg_close (&reply);
}
zmq_close (requester);
zmq_term (context);
return 0;
}

@ -0,0 +1,75 @@
#include <dfhack/Core.h>
#include <dfhack/Console.h>
#include <dfhack/Export.h>
#include <dfhack/PluginManager.h>
#include <zmq.hpp>
using namespace DFHack;
// Here go all the command declarations...
// mostly to allow having the mandatory stuff on top of the file and commands on the bottom
DFhackCExport command_result server (Core * c, std::vector <std::string> & parameters);
// A plugins must be able to return its name. This must correspond to the filename - skeleton.plug.so or skeleton.plug.dll
DFhackCExport const char * plugin_name ( void )
{
return "server";
}
// Mandatory init function. If you have some global state, create it here.
DFhackCExport command_result plugin_init ( Core * c, std::vector <PluginCommand> &commands)
{
// Fill the command list with your commands.
commands.clear();
commands.push_back(PluginCommand("server",
"Inane zeromq example turned into a plugin.",
server));
return CR_OK;
}
// This is called right before the plugin library is removed from memory.
DFhackCExport command_result plugin_shutdown ( Core * c )
{
// You *MUST* kill all threads you created before this returns.
// If everythin fails, just return CR_FAILURE. Your plugin will be
// in a zombie state, but things won't crash.
return CR_OK;
}
// This is WRONG and STUPID. Never use this as an example!
DFhackCExport command_result server (Core * c, std::vector <std::string> & parameters)
{
// It's nice to provide a 'help' option for your command.
// It's also nice to print the same help if you get invalid options from the user instead of just acting strange
for(int i = 0; i < parameters.size();i++)
{
if(parameters[i] == "help" || parameters[i] == "?")
{
// Core has a handle to the console. The console is thread-safe.
// Only one thing can read from it at a time though...
c->con.print("This command is a simple Hello World example for zeromq!\n");
return CR_OK;
}
}
// Prepare our context and socket
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_REP);
socket.bind ("tcp://*:5555");
while (true)
{
zmq::message_t request;
// Wait for next request from client
socket.recv (&request);
c->con.print("Received Hello\n");
// Do some 'work'
sleep (1);
// Send reply back to client
zmq::message_t reply (5);
memcpy ((void *) reply.data (), "World", 5);
socket.send (reply);
}
return CR_OK;
}

Binary file not shown.

@ -0,0 +1,41 @@
# libzmq.la - a libtool library file
# Generated by ltmain.sh (GNU libtool) 2.2.6b Debian-2.2.6b-2ubuntu1
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# The name that we can dlopen(3).
dlname='libzmq.so.1'
# Names of this library.
library_names='libzmq.so.1.0.0 libzmq.so.1 libzmq.so'
# The name of the static archive.
old_library='libzmq.a'
# Linker flags that can not go in dependency_libs.
inherited_linker_flags=''
# Libraries that this one depends upon.
dependency_libs=' -luuid -lrt -lpthread'
# Names of additional weak libraries provided by this library
weak_library_names=''
# Version information for libzmq.
current=1
age=0
revision=0
# Is this an already installed library?
installed=yes
# Should we warn about portability when linking against -modules?
shouldnotlink=no
# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''
# Directory that this library needs to be installed in:
libdir='/home/peterix/zeromq/lib'

Binary file not shown.

@ -0,0 +1,269 @@
/*
Copyright (c) 2007-2011 iMatix Corporation
Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_H_INCLUDED__
#define __ZMQ_H_INCLUDED__
#ifdef __cplusplus
extern "C" {
#endif
#include <errno.h>
#include <stddef.h>
#if defined _WIN32
#include <winsock2.h>
#endif
/* Handle DSO symbol visibility */
#if defined _WIN32
# if defined DLL_EXPORT
# define ZMQ_EXPORT __declspec(dllexport)
# else
# define ZMQ_EXPORT __declspec(dllimport)
# endif
#else
# if defined __SUNPRO_C || defined __SUNPRO_CC
# define ZMQ_EXPORT __global
# elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER
# define ZMQ_EXPORT __attribute__ ((visibility("default")))
# else
# define ZMQ_EXPORT
# endif
#endif
/******************************************************************************/
/* 0MQ versioning support. */
/******************************************************************************/
/* Version macros for compile-time API version detection */
#define ZMQ_VERSION_MAJOR 2
#define ZMQ_VERSION_MINOR 1
#define ZMQ_VERSION_PATCH 10
#define ZMQ_MAKE_VERSION(major, minor, patch) \
((major) * 10000 + (minor) * 100 + (patch))
#define ZMQ_VERSION \
ZMQ_MAKE_VERSION(ZMQ_VERSION_MAJOR, ZMQ_VERSION_MINOR, ZMQ_VERSION_PATCH)
/* Run-time API version detection */
ZMQ_EXPORT void zmq_version (int *major, int *minor, int *patch);
/******************************************************************************/
/* 0MQ errors. */
/******************************************************************************/
/* A number random enough not to collide with different errno ranges on */
/* different OSes. The assumption is that error_t is at least 32-bit type. */
#define ZMQ_HAUSNUMERO 156384712
/* On Windows platform some of the standard POSIX errnos are not defined. */
#ifndef ENOTSUP
#define ENOTSUP (ZMQ_HAUSNUMERO + 1)
#endif
#ifndef EPROTONOSUPPORT
#define EPROTONOSUPPORT (ZMQ_HAUSNUMERO + 2)
#endif
#ifndef ENOBUFS
#define ENOBUFS (ZMQ_HAUSNUMERO + 3)
#endif
#ifndef ENETDOWN
#define ENETDOWN (ZMQ_HAUSNUMERO + 4)
#endif
#ifndef EADDRINUSE
#define EADDRINUSE (ZMQ_HAUSNUMERO + 5)
#endif
#ifndef EADDRNOTAVAIL
#define EADDRNOTAVAIL (ZMQ_HAUSNUMERO + 6)
#endif
#ifndef ECONNREFUSED
#define ECONNREFUSED (ZMQ_HAUSNUMERO + 7)
#endif
#ifndef EINPROGRESS
#define EINPROGRESS (ZMQ_HAUSNUMERO + 8)
#endif
#ifndef ENOTSOCK
#define ENOTSOCK (ZMQ_HAUSNUMERO + 9)
#endif
/* Native 0MQ error codes. */
#define EFSM (ZMQ_HAUSNUMERO + 51)
#define ENOCOMPATPROTO (ZMQ_HAUSNUMERO + 52)
#define ETERM (ZMQ_HAUSNUMERO + 53)
#define EMTHREAD (ZMQ_HAUSNUMERO + 54)
/* This function retrieves the errno as it is known to 0MQ library. The goal */
/* of this function is to make the code 100% portable, including where 0MQ */
/* compiled with certain CRT library (on Windows) is linked to an */
/* application that uses different CRT library. */
ZMQ_EXPORT int zmq_errno (void);
/* Resolves system errors and 0MQ errors to human-readable string. */
ZMQ_EXPORT const char *zmq_strerror (int errnum);
/******************************************************************************/
/* 0MQ message definition. */
/******************************************************************************/
/* Maximal size of "Very Small Message". VSMs are passed by value */
/* to avoid excessive memory allocation/deallocation. */
/* If VMSs larger than 255 bytes are required, type of 'vsm_size' */
/* field in zmq_msg_t structure should be modified accordingly. */
#define ZMQ_MAX_VSM_SIZE 30
/* Message types. These integers may be stored in 'content' member of the */
/* message instead of regular pointer to the data. */
#define ZMQ_DELIMITER 31
#define ZMQ_VSM 32
/* Message flags. ZMQ_MSG_SHARED is strictly speaking not a message flag */
/* (it has no equivalent in the wire format), however, making it a flag */
/* allows us to pack the stucture tigher and thus improve performance. */
#define ZMQ_MSG_MORE 1
#define ZMQ_MSG_SHARED 128
#define ZMQ_MSG_MASK 129 /* Merges all the flags */
/* A message. Note that 'content' is not a pointer to the raw data. */
/* Rather it is pointer to zmq::msg_content_t structure */
/* (see src/msg_content.hpp for its definition). */
typedef struct
{
void *content;
unsigned char flags;
unsigned char vsm_size;
unsigned char vsm_data [ZMQ_MAX_VSM_SIZE];
} zmq_msg_t;
typedef void (zmq_free_fn) (void *data, void *hint);
ZMQ_EXPORT int zmq_msg_init (zmq_msg_t *msg);
ZMQ_EXPORT int zmq_msg_init_size (zmq_msg_t *msg, size_t size);
ZMQ_EXPORT int zmq_msg_init_data (zmq_msg_t *msg, void *data,
size_t size, zmq_free_fn *ffn, void *hint);
ZMQ_EXPORT int zmq_msg_close (zmq_msg_t *msg);
ZMQ_EXPORT int zmq_msg_move (zmq_msg_t *dest, zmq_msg_t *src);
ZMQ_EXPORT int zmq_msg_copy (zmq_msg_t *dest, zmq_msg_t *src);
ZMQ_EXPORT void *zmq_msg_data (zmq_msg_t *msg);
ZMQ_EXPORT size_t zmq_msg_size (zmq_msg_t *msg);
/******************************************************************************/
/* 0MQ infrastructure (a.k.a. context) initialisation & termination. */
/******************************************************************************/
ZMQ_EXPORT void *zmq_init (int io_threads);
ZMQ_EXPORT int zmq_term (void *context);
/******************************************************************************/
/* 0MQ socket definition. */
/******************************************************************************/
/* Socket types. */
#define ZMQ_PAIR 0
#define ZMQ_PUB 1
#define ZMQ_SUB 2
#define ZMQ_REQ 3
#define ZMQ_REP 4
#define ZMQ_DEALER 5
#define ZMQ_ROUTER 6
#define ZMQ_PULL 7
#define ZMQ_PUSH 8
#define ZMQ_XPUB 9
#define ZMQ_XSUB 10
#define ZMQ_XREQ ZMQ_DEALER /* Old alias, remove in 3.x */
#define ZMQ_XREP ZMQ_ROUTER /* Old alias, remove in 3.x */
#define ZMQ_UPSTREAM ZMQ_PULL /* Old alias, remove in 3.x */
#define ZMQ_DOWNSTREAM ZMQ_PUSH /* Old alias, remove in 3.x */
/* Socket options. */
#define ZMQ_HWM 1
#define ZMQ_SWAP 3
#define ZMQ_AFFINITY 4
#define ZMQ_IDENTITY 5
#define ZMQ_SUBSCRIBE 6
#define ZMQ_UNSUBSCRIBE 7
#define ZMQ_RATE 8
#define ZMQ_RECOVERY_IVL 9
#define ZMQ_MCAST_LOOP 10
#define ZMQ_SNDBUF 11
#define ZMQ_RCVBUF 12
#define ZMQ_RCVMORE 13
#define ZMQ_FD 14
#define ZMQ_EVENTS 15
#define ZMQ_TYPE 16
#define ZMQ_LINGER 17
#define ZMQ_RECONNECT_IVL 18
#define ZMQ_BACKLOG 19
#define ZMQ_RECOVERY_IVL_MSEC 20 /* opt. recovery time, reconcile in 3.x */
#define ZMQ_RECONNECT_IVL_MAX 21
/* Send/recv options. */
#define ZMQ_NOBLOCK 1
#define ZMQ_SNDMORE 2
ZMQ_EXPORT void *zmq_socket (void *context, int type);
ZMQ_EXPORT int zmq_close (void *s);
ZMQ_EXPORT int zmq_setsockopt (void *s, int option, const void *optval,
size_t optvallen);
ZMQ_EXPORT int zmq_getsockopt (void *s, int option, void *optval,
size_t *optvallen);
ZMQ_EXPORT int zmq_bind (void *s, const char *addr);
ZMQ_EXPORT int zmq_connect (void *s, const char *addr);
ZMQ_EXPORT int zmq_send (void *s, zmq_msg_t *msg, int flags);
ZMQ_EXPORT int zmq_recv (void *s, zmq_msg_t *msg, int flags);
/******************************************************************************/
/* I/O multiplexing. */
/******************************************************************************/
#define ZMQ_POLLIN 1
#define ZMQ_POLLOUT 2
#define ZMQ_POLLERR 4
typedef struct
{
void *socket;
#if defined _WIN32
SOCKET fd;
#else
int fd;
#endif
short events;
short revents;
} zmq_pollitem_t;
ZMQ_EXPORT int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout);
/******************************************************************************/
/* Built-in devices */
/******************************************************************************/
#define ZMQ_STREAMER 1
#define ZMQ_FORWARDER 2
#define ZMQ_QUEUE 3
ZMQ_EXPORT int zmq_device (int device, void * insocket, void* outsocket);
#undef ZMQ_EXPORT
#ifdef __cplusplus
}
#endif
#endif

@ -0,0 +1,301 @@
/*
Copyright (c) 2007-2011 iMatix Corporation
Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_HPP_INCLUDED__
#define __ZMQ_HPP_INCLUDED__
#include "zmq.h"
#include <cassert>
#include <cstring>
#include <exception>
namespace zmq
{
typedef zmq_free_fn free_fn;
typedef zmq_pollitem_t pollitem_t;
class error_t : public std::exception
{
public:
error_t () : errnum (zmq_errno ()) {}
virtual const char *what () const throw ()
{
return zmq_strerror (errnum);
}
int num () const
{
return errnum;
}
private:
int errnum;
};
inline int poll (zmq_pollitem_t *items_, int nitems_, long timeout_ = -1)
{
int rc = zmq_poll (items_, nitems_, timeout_);
if (rc < 0)
throw error_t ();
return rc;
}
inline void device (int device_, void * insocket_, void* outsocket_)
{
int rc = zmq_device (device_, insocket_, outsocket_);
if (rc != 0)
throw error_t ();
}
inline void version (int *major_, int *minor_, int *patch_)
{
zmq_version (major_, minor_, patch_);
}
class message_t : private zmq_msg_t
{
friend class socket_t;
public:
inline message_t ()
{
int rc = zmq_msg_init (this);
if (rc != 0)
throw error_t ();
}
inline message_t (size_t size_)
{
int rc = zmq_msg_init_size (this, size_);
if (rc != 0)
throw error_t ();
}
inline message_t (void *data_, size_t size_, free_fn *ffn_,
void *hint_ = NULL)
{
int rc = zmq_msg_init_data (this, data_, size_, ffn_, hint_);
if (rc != 0)
throw error_t ();
}
inline ~message_t ()
{
int rc = zmq_msg_close (this);
assert (rc == 0);
}
inline void rebuild ()
{
int rc = zmq_msg_close (this);
if (rc != 0)
throw error_t ();
rc = zmq_msg_init (this);
if (rc != 0)
throw error_t ();
}
inline void rebuild (size_t size_)
{
int rc = zmq_msg_close (this);
if (rc != 0)
throw error_t ();
rc = zmq_msg_init_size (this, size_);
if (rc != 0)
throw error_t ();
}
inline void rebuild (void *data_, size_t size_, free_fn *ffn_,
void *hint_ = NULL)
{
int rc = zmq_msg_close (this);
if (rc != 0)
throw error_t ();
rc = zmq_msg_init_data (this, data_, size_, ffn_, hint_);
if (rc != 0)
throw error_t ();
}
inline void move (message_t *msg_)
{
int rc = zmq_msg_move (this, (zmq_msg_t*) msg_);
if (rc != 0)
throw error_t ();
}
inline void copy (message_t *msg_)
{
int rc = zmq_msg_copy (this, (zmq_msg_t*) msg_);
if (rc != 0)
throw error_t ();
}
inline void *data ()
{
return zmq_msg_data (this);
}
inline size_t size ()
{
return zmq_msg_size (this);
}
private:
// Disable implicit message copying, so that users won't use shared
// messages (less efficient) without being aware of the fact.
message_t (const message_t&);
void operator = (const message_t&);
};
class context_t
{
friend class socket_t;
public:
inline context_t (int io_threads_)
{
ptr = zmq_init (io_threads_);
if (ptr == NULL)
throw error_t ();
}
inline ~context_t ()
{
int rc = zmq_term (ptr);
assert (rc == 0);
}
// Be careful with this, it's probably only useful for
// using the C api together with an existing C++ api.
// Normally you should never need to use this.
inline operator void* ()
{
return ptr;
}
private:
void *ptr;
context_t (const context_t&);
void operator = (const context_t&);
};
class socket_t
{
public:
inline socket_t (context_t &context_, int type_)
{
ptr = zmq_socket (context_.ptr, type_);
if (ptr == NULL)
throw error_t ();
}
inline ~socket_t ()
{
close();
}
inline operator void* ()
{
return ptr;
}
inline void close()
{
if(ptr == NULL)
// already closed
return ;
int rc = zmq_close (ptr);
if (rc != 0)
throw error_t ();
ptr = 0 ;
}
inline void setsockopt (int option_, const void *optval_,
size_t optvallen_)
{
int rc = zmq_setsockopt (ptr, option_, optval_, optvallen_);
if (rc != 0)
throw error_t ();
}
inline void getsockopt (int option_, void *optval_,
size_t *optvallen_)
{
int rc = zmq_getsockopt (ptr, option_, optval_, optvallen_);
if (rc != 0)
throw error_t ();
}
inline void bind (const char *addr_)
{
int rc = zmq_bind (ptr, addr_);
if (rc != 0)
throw error_t ();
}
inline void connect (const char *addr_)
{
int rc = zmq_connect (ptr, addr_);
if (rc != 0)
throw error_t ();
}
inline bool send (message_t &msg_, int flags_ = 0)
{
int rc = zmq_send (ptr, &msg_, flags_);
if (rc == 0)
return true;
if (rc == -1 && zmq_errno () == EAGAIN)
return false;
throw error_t ();
}
inline bool recv (message_t *msg_, int flags_ = 0)
{
int rc = zmq_recv (ptr, msg_, flags_);
if (rc == 0)
return true;
if (rc == -1 && zmq_errno () == EAGAIN)
return false;
throw error_t ();
}
private:
void *ptr;
socket_t (const socket_t&);
void operator = (const socket_t&);
};
}
#endif

@ -0,0 +1,64 @@
/*
Copyright (c) 2007-2011 iMatix Corporation
Copyright (c) 2007-2011 Other contributors as noted in the AUTHORS file
This file is part of 0MQ.
0MQ is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
0MQ is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __ZMQ_UTILS_H_INCLUDED__
#define __ZMQ_UTILS_H_INCLUDED__
#ifdef __cplusplus
extern "C" {
#endif
/* Handle DSO symbol visibility */
#if defined _WIN32
# if defined DLL_EXPORT
# define ZMQ_EXPORT __declspec(dllexport)
# else
# define ZMQ_EXPORT __declspec(dllimport)
# endif
#else
# if defined __SUNPRO_C || defined __SUNPRO_CC
# define ZMQ_EXPORT __global
# elif (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER
# define ZMQ_EXPORT __attribute__ ((visibility("default")))
# else
# define ZMQ_EXPORT
# endif
#endif
/* Helper functions are used by perf tests so that they don't have to care */
/* about minutiae of time-related functions on different OS platforms. */
/* Starts the stopwatch. Returns the handle to the watch. */
ZMQ_EXPORT void *zmq_stopwatch_start (void);
/* Stops the stopwatch. Returns the number of microseconds elapsed since */
/* the stopwatch was started. */
ZMQ_EXPORT unsigned long zmq_stopwatch_stop (void *watch_);
/* Sleeps for specified number of seconds. */
ZMQ_EXPORT void zmq_sleep (int seconds_);
#undef ZMQ_EXPORT
#ifdef __cplusplus
}
#endif
#endif