From 6f2430ef7bbd2e9fbe0b933fb20056227900bb1f Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Fri, 19 Jul 2024 15:17:39 -0400 Subject: [PATCH] Allow for autogen bypass, partial fix for #264 #252 #207, ref #303 Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com> --- CMakeLists.txt | 34 ++- cpp/cmake/modules/Findcsp_autogen.cmake | 32 ++- .../websocket/csp_autogen/websocket_types.cpp | 87 ++++++ .../websocket/csp_autogen/websocket_types.h | 225 ++++++++++++++++ cpp/csp/engine/csp_autogen/autogen_types.cpp | 112 ++++++++ cpp/csp/engine/csp_autogen/autogen_types.h | 251 ++++++++++++++++++ csp/build/csp_autogen.py | 116 +++++--- csp/tests/build/test_pregenerated_types.py | 66 +++++ 8 files changed, 856 insertions(+), 67 deletions(-) create mode 100644 cpp/csp/adapters/websocket/csp_autogen/websocket_types.cpp create mode 100644 cpp/csp/adapters/websocket/csp_autogen/websocket_types.h create mode 100644 cpp/csp/engine/csp_autogen/autogen_types.cpp create mode 100644 cpp/csp/engine/csp_autogen/autogen_types.h create mode 100644 csp/tests/build/test_pregenerated_types.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bb693281..4435ecf30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -243,8 +243,10 @@ function(csp_autogen MODULE_NAME DEST_FILENAME HEADER_NAME_OUTVAR SOURCE_NAME_OU cmake_path(SET CSP_AUTOGEN_MODULE_PATH NORMALIZE "${CMAKE_SOURCE_DIR}/csp/build/csp_autogen.py") cmake_path(SET CSP_AUTOGEN_DESTINATION_FOLDER NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen") - cmake_path(SET CSP_AUTOTGEN_CPP_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.cpp") - cmake_path(SET CSP_AUTOTGEN_H_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.h") + cmake_path(SET CSP_AUTOGEN_CPP_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.cpp") + cmake_path(SET CSP_AUTOGEN_CPP_MAYBE_EXISTING NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/csp_autogen/${DEST_FILENAME}.cpp") + cmake_path(SET CSP_AUTOGEN_H_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.h") + cmake_path(SET CSP_AUTOGEN_H_MAYBE_EXISTING NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/csp_autogen/${DEST_FILENAME}.h") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(CSP_AUTOGEN_PYTHONPATH ${PROJECT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE};${CMAKE_SOURCE_DIR};%PYTHONPATH% ) @@ -252,17 +254,23 @@ function(csp_autogen MODULE_NAME DEST_FILENAME HEADER_NAME_OUTVAR SOURCE_NAME_OU set(CSP_AUTOGEN_PYTHONPATH ${PROJECT_BINARY_DIR}/lib:${CMAKE_SOURCE_DIR}:$$PYTHONPATH ) endif() - add_custom_command(OUTPUT "${CSP_AUTOTGEN_CPP_OUT}" "${CSP_AUTOTGEN_H_OUT}" - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CSP_AUTOGEN_PYTHONPATH}" ${Python_EXECUTABLE} ${CSP_AUTOGEN_MODULE_PATH} -m ${MODULE_NAME} -d ${CSP_AUTOGEN_DESTINATION_FOLDER} -o ${DEST_FILENAME} ${CSP_AUTOGEN_EXTRA_ARGS} - COMMENT "generating csp c++ types from module ${MODULE_NAME}" - DEPENDS mkdir_autogen_${MODULE_NAME} - ${CMAKE_SOURCE_DIR}/csp/build/csp_autogen.py - ${CMAKE_SOURCE_DIR}/${MODULE_FILENAME} - csptypesimpl - ) - - set(${SOURCE_NAME_OUTVAR} "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.cpp" PARENT_SCOPE ) - set(${HEADER_NAME_OUTVAR} "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.h" PARENT_SCOPE ) + if (EXISTS "${CSP_AUTOGEN_CPP_MAYBE_EXISTING}" AND EXISTS "${CSP_AUTOGEN_H_MAYBE_EXISTING}") + # Files exist in-source + set(${SOURCE_NAME_OUTVAR} "${CSP_AUTOGEN_CPP_MAYBE_EXISTING}" PARENT_SCOPE ) + set(${HEADER_NAME_OUTVAR} "${CSP_AUTOGEN_H_MAYBE_EXISTING}" PARENT_SCOPE ) + else() + add_custom_command(OUTPUT "${CSP_AUTOGEN_CPP_OUT}" "${CSP_AUTOGEN_H_OUT}" + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CSP_AUTOGEN_PYTHONPATH}" ${Python_EXECUTABLE} ${CSP_AUTOGEN_MODULE_PATH} -m ${MODULE_NAME} -d ${CSP_AUTOGEN_DESTINATION_FOLDER} -o ${DEST_FILENAME} ${CSP_AUTOGEN_EXTRA_ARGS} + COMMENT "generating csp c++ types from module ${MODULE_NAME}" + DEPENDS mkdir_autogen_${MODULE_NAME} + ${CMAKE_SOURCE_DIR}/csp/build/csp_autogen.py + ${CMAKE_SOURCE_DIR}/${MODULE_FILENAME} + csptypesimpl + ) + + set(${SOURCE_NAME_OUTVAR} "${CSP_AUTOGEN_CPP_OUT}" PARENT_SCOPE ) + set(${HEADER_NAME_OUTVAR} "${CSP_AUTOGEN_H_OUT}" PARENT_SCOPE ) + endif() endfunction() diff --git a/cpp/cmake/modules/Findcsp_autogen.cmake b/cpp/cmake/modules/Findcsp_autogen.cmake index 1c28a99f6..4e7588308 100644 --- a/cpp/cmake/modules/Findcsp_autogen.cmake +++ b/cpp/cmake/modules/Findcsp_autogen.cmake @@ -15,8 +15,10 @@ function(csp_autogen MODULE_NAME DEST_FILENAME HEADER_NAME_OUTVAR SOURCE_NAME_OU cmake_path(SET CSP_AUTOGEN_MODULE_PATH NORMALIZE "${CMAKE_SOURCE_DIR}/csp/build/csp_autogen.py") cmake_path(SET CSP_AUTOGEN_DESTINATION_FOLDER NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen") - cmake_path(SET CSP_AUTOTGEN_CPP_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.cpp") - cmake_path(SET CSP_AUTOTGEN_H_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.h") + cmake_path(SET CSP_AUTOGEN_CPP_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.cpp") + cmake_path(SET CSP_AUTOGEN_CPP_MAYBE_EXISTING NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/csp_autogen/${DEST_FILENAME}.cpp") + cmake_path(SET CSP_AUTOGEN_H_OUT NORMALIZE "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.h") + cmake_path(SET CSP_AUTOGEN_H_MAYBE_EXISTING NORMALIZE "${CMAKE_CURRENT_SOURCE_DIR}/csp_autogen/${DEST_FILENAME}.h") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") set(CSP_AUTOGEN_PYTHONPATH ${PROJECT_BINARY_DIR}/lib/${CMAKE_BUILD_TYPE};${CMAKE_SOURCE_DIR};%PYTHONPATH% ) @@ -24,15 +26,21 @@ function(csp_autogen MODULE_NAME DEST_FILENAME HEADER_NAME_OUTVAR SOURCE_NAME_OU set(CSP_AUTOGEN_PYTHONPATH ${PROJECT_BINARY_DIR}/lib:${CMAKE_SOURCE_DIR}:$$PYTHONPATH ) endif() - add_custom_command(OUTPUT "${CSP_AUTOTGEN_CPP_OUT}" "${CSP_AUTOTGEN_H_OUT}" - COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CSP_AUTOGEN_PYTHONPATH}" ${Python_EXECUTABLE} ${CSP_AUTOGEN_MODULE_PATH} -m ${MODULE_NAME} -d ${CSP_AUTOGEN_DESTINATION_FOLDER} -o ${DEST_FILENAME} ${CSP_AUTOGEN_EXTRA_ARGS} - COMMENT "generating csp c++ types from module ${MODULE_NAME}" - DEPENDS mkdir_autogen_${MODULE_NAME} - ${CMAKE_SOURCE_DIR}/csp/build/csp_autogen.py - ${CMAKE_SOURCE_DIR}/${MODULE_FILENAME} - csptypesimpl - ) + if (EXISTS "${CSP_AUTOGEN_CPP_MAYBE_EXISTING}" AND EXISTS "${CSP_AUTOGEN_H_MAYBE_EXISTING}") + # Files exist in-source + set(${SOURCE_NAME_OUTVAR} "${CSP_AUTOGEN_CPP_MAYBE_EXISTING}" PARENT_SCOPE ) + set(${HEADER_NAME_OUTVAR} "${CSP_AUTOGEN_H_MAYBE_EXISTING}" PARENT_SCOPE ) + else() + add_custom_command(OUTPUT "${CSP_AUTOGEN_CPP_OUT}" "${CSP_AUTOGEN_H_OUT}" + COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CSP_AUTOGEN_PYTHONPATH}" ${Python_EXECUTABLE} ${CSP_AUTOGEN_MODULE_PATH} -m ${MODULE_NAME} -d ${CSP_AUTOGEN_DESTINATION_FOLDER} -o ${DEST_FILENAME} ${CSP_AUTOGEN_EXTRA_ARGS} + COMMENT "generating csp c++ types from module ${MODULE_NAME}" + DEPENDS mkdir_autogen_${MODULE_NAME} + ${CMAKE_SOURCE_DIR}/csp/build/csp_autogen.py + ${CMAKE_SOURCE_DIR}/${MODULE_FILENAME} + csptypesimpl + ) - set(${SOURCE_NAME_OUTVAR} "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.cpp" PARENT_SCOPE ) - set(${HEADER_NAME_OUTVAR} "${CMAKE_CURRENT_BINARY_DIR}/csp_autogen/${DEST_FILENAME}.h" PARENT_SCOPE ) + set(${SOURCE_NAME_OUTVAR} "${CSP_AUTOGEN_CPP_OUT}" PARENT_SCOPE ) + set(${HEADER_NAME_OUTVAR} "${CSP_AUTOGEN_H_OUT}" PARENT_SCOPE ) + endif() endfunction() \ No newline at end of file diff --git a/cpp/csp/adapters/websocket/csp_autogen/websocket_types.cpp b/cpp/csp/adapters/websocket/csp_autogen/websocket_types.cpp new file mode 100644 index 000000000..ad01b6ebf --- /dev/null +++ b/cpp/csp/adapters/websocket/csp_autogen/websocket_types.cpp @@ -0,0 +1,87 @@ + +// AUTOGENERATED BY CSP_AUTOGEN +// DO NOT MODIFY DIRECTLY +// command: python csp/build/csp_autogen.py -m csp.adapters.websocket_types -d cpp/csp/adapters/websocket/csp_autogen/ -o websocket_types --omit_asserts + +#include "websocket_types.h" +#include +#include +#include +#include +#include +#include + +namespace csp::autogen +{ + +#define _offsetof( C, M ) ( ( char * ) &( ( C * ) nullptr ) -> M - ( char * ) 0 ) + +static void assert_or_die( bool assertion, const char * error ) +{ + if( !assertion ) + { + std::cerr << "Fatal error on import of " << __FILE__ << ": " << error << std::endl; + if( PyErr_Occurred() ) + PyErr_Print(); + abort(); + } +} + + +bool WebsocketStatus::static_init() +{ + if( Py_IsInitialized() ) + { + csp::python::AcquireGIL gil; + + // initialize EnumMeta from python type if we're in python + PyObject * pymodule = PyImport_ImportModule( "csp.adapters.websocket_types" ); + assert_or_die( pymodule != nullptr, "failed to import struct module csp.adapters.websocket_types" ); + + PyObject * enumType = PyObject_GetAttrString(pymodule, "WebsocketStatus" ); + assert_or_die( enumType != nullptr, "failed to find num type WebsocketStatus in module csp.adapters.websocket_types" ); + + // should add some assertion here.. + csp::python::PyCspEnumMeta * pymeta = ( csp::python::PyCspEnumMeta * ) enumType; + s_meta = pymeta -> enumMeta; + } + + return true; +} + +bool static_init_WebsocketStatus = WebsocketStatus::static_init(); +std::shared_ptr WebsocketStatus::s_meta; +WebsocketStatus WebsocketStatus::ACTIVE = WebsocketStatus::create("ACTIVE"); +WebsocketStatus WebsocketStatus::GENERIC_ERROR = WebsocketStatus::create("GENERIC_ERROR"); +WebsocketStatus WebsocketStatus::CONNECTION_FAILED = WebsocketStatus::create("CONNECTION_FAILED"); +WebsocketStatus WebsocketStatus::CLOSED = WebsocketStatus::create("CLOSED"); +WebsocketStatus WebsocketStatus::MESSAGE_SEND_FAIL = WebsocketStatus::create("MESSAGE_SEND_FAIL"); + + +bool WebsocketHeaderUpdate::static_init() +{ + + if( Py_IsInitialized() ) + { + //Note that windows requires we grab the GIL since the windows DLL loading code releases GIL + csp::python::AcquireGIL gil; + + // initialize StructMeta from python type if we're in python + PyObject * pymodule = PyImport_ImportModule( "csp.adapters.websocket_types" ); + assert_or_die( pymodule != nullptr, "failed to import struct module csp.adapters.websocket_types" ); + + PyObject * structType = PyObject_GetAttrString(pymodule, "WebsocketHeaderUpdate" ); + assert_or_die( structType != nullptr, "failed to find struct type WebsocketHeaderUpdate in module csp.adapters.websocket_types" ); + + // should add some assertion here.. + csp::python::PyStructMeta * pymeta = ( csp::python::PyStructMeta * ) structType; + s_meta = pymeta -> structMeta; + } + + return true; +} + +bool static_init_WebsocketHeaderUpdate = WebsocketHeaderUpdate::static_init(); +csp::StructMetaPtr WebsocketHeaderUpdate::s_meta; + +} diff --git a/cpp/csp/adapters/websocket/csp_autogen/websocket_types.h b/cpp/csp/adapters/websocket/csp_autogen/websocket_types.h new file mode 100644 index 000000000..a9912bc4c --- /dev/null +++ b/cpp/csp/adapters/websocket/csp_autogen/websocket_types.h @@ -0,0 +1,225 @@ + +// AUTOGENERATED BY CSP_AUTOGEN +// DO NOT MODIFY DIRECTLY +// command: python csp/build/csp_autogen.py -m csp.adapters.websocket_types -d cpp/csp/adapters/websocket/csp_autogen/ -o websocket_types --omit_asserts + +#ifndef _IN_CSP_AUTOGEN_CSP_ADAPTERS_WEBSOCKET_TYPES +#define _IN_CSP_AUTOGEN_CSP_ADAPTERS_WEBSOCKET_TYPES + +#include +#include +#include +#include + +namespace csp::autogen +{ + +class CSP_PUBLIC WebsocketStatus : public csp::CspEnum +{ +public: + // Raw value quick access + enum class enum_ + { + ACTIVE = 0, + GENERIC_ERROR = 1, + CONNECTION_FAILED = 2, + CLOSED = 3, + MESSAGE_SEND_FAIL = 4 + }; + + // CspEnum types + static WebsocketStatus ACTIVE; + static WebsocketStatus GENERIC_ERROR; + static WebsocketStatus CONNECTION_FAILED; + static WebsocketStatus CLOSED; + static WebsocketStatus MESSAGE_SEND_FAIL; + + const char * asCString() const { return name().c_str(); } + const std::string & asString() const { return name(); } + + static WebsocketStatus create( enum_ v ) { return s_meta -> create( ( int64_t ) v ); } + static WebsocketStatus create( const char * name) { return s_meta -> fromString( name ); } + static WebsocketStatus create( const std::string & s ) { return create( s.c_str() ); } + + enum_ enum_value() const { return ( enum_ ) value(); } + + static constexpr uint32_t num_types() { return 5; } + + static bool static_init(); + + WebsocketStatus( const csp::CspEnum & v ) : csp::CspEnum( v ) { CSP_TRUE_OR_THROW( v.meta() == s_meta.get(), AssertionError, "Mismatched enum meta" ); } + +private: + + static std::shared_ptr s_meta; +}; + +class CSP_PUBLIC WebsocketHeaderUpdate : public csp::Struct +{ +public: + + using Ptr = csp::TypedStructPtr; + + WebsocketHeaderUpdate() = delete; + ~WebsocketHeaderUpdate() = delete; + WebsocketHeaderUpdate( const WebsocketHeaderUpdate & ) = delete; + WebsocketHeaderUpdate( WebsocketHeaderUpdate && ) = delete; + + Ptr copy() const { return csp::structptr_cast( Struct::copy() ); } + + static WebsocketHeaderUpdate::Ptr create() + { + return Ptr( static_cast( s_meta -> createRaw() ) ); + } + + static const csp::StructMetaPtr & meta() { return s_meta; } + + + const std::string & key() const + { + + + + + if( !key_isSet() ) + CSP_THROW( csp::ValueError, "field key on struct WebsocketHeaderUpdate is not set" ); + + return m_key; + } + + void set_key( const std::string & value ) + { + + + + + + m_WebsocketHeaderUpdate_mask[0] |= 1; + + + //TODO employ move semantics where it makes sense + m_key = value; + } + + + void set_key( const char * value ) + { + + + + + + m_WebsocketHeaderUpdate_mask[0] |= 1; + + m_key = value; + } + + void set_key( std::string_view value ) + { + + + + + + m_WebsocketHeaderUpdate_mask[0] |= 1; + + m_key = value; + } + + + bool key_isSet() const + { + + return m_WebsocketHeaderUpdate_mask[0] & 1; + } + + void clear_key() + { + + m_WebsocketHeaderUpdate_mask[0] &= ~1; + } + + const std::string & value() const + { + + + + + if( !value_isSet() ) + CSP_THROW( csp::ValueError, "field value on struct WebsocketHeaderUpdate is not set" ); + + return m_value; + } + + void set_value( const std::string & value ) + { + + + + + + m_WebsocketHeaderUpdate_mask[0] |= 2; + + + //TODO employ move semantics where it makes sense + m_value = value; + } + + + void set_value( const char * value ) + { + + + + + + m_WebsocketHeaderUpdate_mask[0] |= 2; + + m_value = value; + } + + void set_value( std::string_view value ) + { + + + + + + m_WebsocketHeaderUpdate_mask[0] |= 2; + + m_value = value; + } + + + bool value_isSet() const + { + + return m_WebsocketHeaderUpdate_mask[0] & 2; + } + + void clear_value() + { + + m_WebsocketHeaderUpdate_mask[0] &= ~2; + } + + + static bool static_init(); + +private: + + std::string m_key; + std::string m_value; + char m_WebsocketHeaderUpdate_mask[1]; + + + static csp::StructMetaPtr s_meta; + + static void assert_mask() + { + + } +}; + +} +#endif \ No newline at end of file diff --git a/cpp/csp/engine/csp_autogen/autogen_types.cpp b/cpp/csp/engine/csp_autogen/autogen_types.cpp new file mode 100644 index 000000000..4b693e22d --- /dev/null +++ b/cpp/csp/engine/csp_autogen/autogen_types.cpp @@ -0,0 +1,112 @@ + +// AUTOGENERATED BY CSP_AUTOGEN +// DO NOT MODIFY DIRECTLY +// command: python csp/build/csp_autogen.py -m csp.impl.types.autogen_types -d cpp/csp/engine/csp_autogen -o autogen_types --omit_asserts + +#include "autogen_types.h" +#include +#include +#include +#include +#include +#include + +namespace csp::autogen +{ + +#define _offsetof( C, M ) ( ( char * ) &( ( C * ) nullptr ) -> M - ( char * ) 0 ) + +static void assert_or_die( bool assertion, const char * error ) +{ + if( !assertion ) + { + std::cerr << "Fatal error on import of " << __FILE__ << ": " << error << std::endl; + if( PyErr_Occurred() ) + PyErr_Print(); + abort(); + } +} + + +bool TimeIndexPolicy::static_init() +{ + if( Py_IsInitialized() ) + { + csp::python::AcquireGIL gil; + + // initialize EnumMeta from python type if we're in python + PyObject * pymodule = PyImport_ImportModule( "csp.impl.types.autogen_types" ); + assert_or_die( pymodule != nullptr, "failed to import struct module csp.impl.types.autogen_types" ); + + PyObject * enumType = PyObject_GetAttrString(pymodule, "TimeIndexPolicy" ); + assert_or_die( enumType != nullptr, "failed to find num type TimeIndexPolicy in module csp.impl.types.autogen_types" ); + + // should add some assertion here.. + csp::python::PyCspEnumMeta * pymeta = ( csp::python::PyCspEnumMeta * ) enumType; + s_meta = pymeta -> enumMeta; + } + + return true; +} + +bool static_init_TimeIndexPolicy = TimeIndexPolicy::static_init(); +std::shared_ptr TimeIndexPolicy::s_meta; +TimeIndexPolicy TimeIndexPolicy::INCLUSIVE = TimeIndexPolicy::create("INCLUSIVE"); +TimeIndexPolicy TimeIndexPolicy::EXCLUSIVE = TimeIndexPolicy::create("EXCLUSIVE"); +TimeIndexPolicy TimeIndexPolicy::EXTRAPOLATE = TimeIndexPolicy::create("EXTRAPOLATE"); + + +bool DynamicBasketEvent::static_init() +{ + + if( Py_IsInitialized() ) + { + //Note that windows requires we grab the GIL since the windows DLL loading code releases GIL + csp::python::AcquireGIL gil; + + // initialize StructMeta from python type if we're in python + PyObject * pymodule = PyImport_ImportModule( "csp.impl.types.autogen_types" ); + assert_or_die( pymodule != nullptr, "failed to import struct module csp.impl.types.autogen_types" ); + + PyObject * structType = PyObject_GetAttrString(pymodule, "DynamicBasketEvent" ); + assert_or_die( structType != nullptr, "failed to find struct type DynamicBasketEvent in module csp.impl.types.autogen_types" ); + + // should add some assertion here.. + csp::python::PyStructMeta * pymeta = ( csp::python::PyStructMeta * ) structType; + s_meta = pymeta -> structMeta; + } + + return true; +} + +bool static_init_DynamicBasketEvent = DynamicBasketEvent::static_init(); +csp::StructMetaPtr DynamicBasketEvent::s_meta; + + +bool DynamicBasketEvents::static_init() +{ + + if( Py_IsInitialized() ) + { + //Note that windows requires we grab the GIL since the windows DLL loading code releases GIL + csp::python::AcquireGIL gil; + + // initialize StructMeta from python type if we're in python + PyObject * pymodule = PyImport_ImportModule( "csp.impl.types.autogen_types" ); + assert_or_die( pymodule != nullptr, "failed to import struct module csp.impl.types.autogen_types" ); + + PyObject * structType = PyObject_GetAttrString(pymodule, "DynamicBasketEvents" ); + assert_or_die( structType != nullptr, "failed to find struct type DynamicBasketEvents in module csp.impl.types.autogen_types" ); + + // should add some assertion here.. + csp::python::PyStructMeta * pymeta = ( csp::python::PyStructMeta * ) structType; + s_meta = pymeta -> structMeta; + } + + return true; +} + +bool static_init_DynamicBasketEvents = DynamicBasketEvents::static_init(); +csp::StructMetaPtr DynamicBasketEvents::s_meta; + +} diff --git a/cpp/csp/engine/csp_autogen/autogen_types.h b/cpp/csp/engine/csp_autogen/autogen_types.h new file mode 100644 index 000000000..25ee20759 --- /dev/null +++ b/cpp/csp/engine/csp_autogen/autogen_types.h @@ -0,0 +1,251 @@ + +// AUTOGENERATED BY CSP_AUTOGEN +// DO NOT MODIFY DIRECTLY +// command: python csp/build/csp_autogen.py -m csp.impl.types.autogen_types -d cpp/csp/engine/csp_autogen -o autogen_types --omit_asserts + +#ifndef _IN_CSP_AUTOGEN_CSP_IMPL_TYPES_AUTOGEN_TYPES +#define _IN_CSP_AUTOGEN_CSP_IMPL_TYPES_AUTOGEN_TYPES + +#include +#include +#include +#include + +namespace csp::autogen +{ + +class CSP_PUBLIC TimeIndexPolicy : public csp::CspEnum +{ +public: + // Raw value quick access + enum class enum_ + { + INCLUSIVE = 1, + EXCLUSIVE = 2, + EXTRAPOLATE = 3 + }; + + // CspEnum types + static TimeIndexPolicy INCLUSIVE; + static TimeIndexPolicy EXCLUSIVE; + static TimeIndexPolicy EXTRAPOLATE; + + const char * asCString() const { return name().c_str(); } + const std::string & asString() const { return name(); } + + static TimeIndexPolicy create( enum_ v ) { return s_meta -> create( ( int64_t ) v ); } + static TimeIndexPolicy create( const char * name) { return s_meta -> fromString( name ); } + static TimeIndexPolicy create( const std::string & s ) { return create( s.c_str() ); } + + enum_ enum_value() const { return ( enum_ ) value(); } + + static constexpr uint32_t num_types() { return 3; } + + static bool static_init(); + + TimeIndexPolicy( const csp::CspEnum & v ) : csp::CspEnum( v ) { CSP_TRUE_OR_THROW( v.meta() == s_meta.get(), AssertionError, "Mismatched enum meta" ); } + +private: + + static std::shared_ptr s_meta; +}; + +class CSP_PUBLIC DynamicBasketEvent : public csp::Struct +{ +public: + + using Ptr = csp::TypedStructPtr; + + DynamicBasketEvent() = delete; + ~DynamicBasketEvent() = delete; + DynamicBasketEvent( const DynamicBasketEvent & ) = delete; + DynamicBasketEvent( DynamicBasketEvent && ) = delete; + + Ptr copy() const { return csp::structptr_cast( Struct::copy() ); } + + static DynamicBasketEvent::Ptr create() + { + return Ptr( static_cast( s_meta -> createRaw() ) ); + } + + static const csp::StructMetaPtr & meta() { return s_meta; } + + + const csp::DialectGenericType & key() const + { + + + + + if( !key_isSet() ) + CSP_THROW( csp::ValueError, "field key on struct DynamicBasketEvent is not set" ); + + return m_key; + } + + void set_key( const csp::DialectGenericType & value ) + { + + + + + + m_DynamicBasketEvent_mask[0] |= 1; + + + //TODO employ move semantics where it makes sense + m_key = value; + } + + + + bool key_isSet() const + { + + return m_DynamicBasketEvent_mask[0] & 1; + } + + void clear_key() + { + + m_DynamicBasketEvent_mask[0] &= ~1; + } + + const bool & added() const + { + + + + + if( !added_isSet() ) + CSP_THROW( csp::ValueError, "field added on struct DynamicBasketEvent is not set" ); + + return m_added; + } + + void set_added( const bool & value ) + { + + + + + + m_DynamicBasketEvent_mask[0] |= 2; + + + //TODO employ move semantics where it makes sense + m_added = value; + } + + + + bool added_isSet() const + { + + return m_DynamicBasketEvent_mask[0] & 2; + } + + void clear_added() + { + + m_DynamicBasketEvent_mask[0] &= ~2; + } + + + static bool static_init(); + +private: + + csp::DialectGenericType m_key; + bool m_added; + char m_DynamicBasketEvent_mask[1]; + + + static csp::StructMetaPtr s_meta; + + static void assert_mask() + { + + } +}; + +class CSP_PUBLIC DynamicBasketEvents : public csp::Struct +{ +public: + + using Ptr = csp::TypedStructPtr; + + DynamicBasketEvents() = delete; + ~DynamicBasketEvents() = delete; + DynamicBasketEvents( const DynamicBasketEvents & ) = delete; + DynamicBasketEvents( DynamicBasketEvents && ) = delete; + + Ptr copy() const { return csp::structptr_cast( Struct::copy() ); } + + static DynamicBasketEvents::Ptr create() + { + return Ptr( static_cast( s_meta -> createRaw() ) ); + } + + static const csp::StructMetaPtr & meta() { return s_meta; } + + + const std::vector & events() const + { + + + + + if( !events_isSet() ) + CSP_THROW( csp::ValueError, "field events on struct DynamicBasketEvents is not set" ); + + return m_events; + } + + void set_events( const std::vector & value ) + { + + + + + + m_DynamicBasketEvents_mask[0] |= 1; + + + //TODO employ move semantics where it makes sense + m_events = value; + } + + + + bool events_isSet() const + { + + return m_DynamicBasketEvents_mask[0] & 1; + } + + void clear_events() + { + + m_DynamicBasketEvents_mask[0] &= ~1; + } + + + static bool static_init(); + +private: + + std::vector m_events; + char m_DynamicBasketEvents_mask[1]; + + + static csp::StructMetaPtr s_meta; + + static void assert_mask() + { + + } +}; + +} +#endif \ No newline at end of file diff --git a/csp/build/csp_autogen.py b/csp/build/csp_autogen.py index 71b570dd3..2f30aca16 100644 --- a/csp/build/csp_autogen.py +++ b/csp/build/csp_autogen.py @@ -2,6 +2,7 @@ import importlib import importlib.util import importlib.machinery +import logging import os.path import sys import types @@ -14,8 +15,11 @@ csp_mod = importlib.util.module_from_spec(spec) sys.modules["csp"] = csp_mod -from csp.impl.struct import Struct # noqa: E402 -from csp.impl.enum import Enum # noqa: E402 + +_GEN_COMMAND = f"""// AUTOGENERATED BY CSP_AUTOGEN +// DO NOT MODIFY DIRECTLY +// command: python {" ".join(sys.argv)} +""" def struct_type(type_info): @@ -58,13 +62,16 @@ def array_type(type_info): class CodeGenerator: - def __init__(self, module_name: str, output_filename: str, namespace: str, generate_imported_types: bool): + def __init__( + self, module_name: str, output_filename: str, namespace: str, generate_imported_types: bool, omit_asserts: bool + ): self._module_name = module_name self._module = importlib.import_module(module_name) self._namespace = namespace self._header_filename = f"{output_filename}.h" self._cpp_filename = f"{output_filename}.cpp" + self._omit_asserts = omit_asserts self._struct_types = [] self._enum_types = [] @@ -80,6 +87,9 @@ def __init__(self, module_name: str, output_filename: str, namespace: str, gener self._external_types[v] = importlib.import_module(v.__module__) continue + from csp.impl.struct import Struct + from csp.impl.enum import Enum + if issubclass(v, Struct) and v is not Struct: self._struct_types.append(v) elif issubclass(v, Enum) and v is not Enum: @@ -87,6 +97,8 @@ def __init__(self, module_name: str, output_filename: str, namespace: str, gener def _get_dependent_headers(self): """see if there are any dependent headers we need to include""" + from csp.impl.struct import Struct + headers = set() for struct_type in self._struct_types: metainfo = struct_type._metadata_info() @@ -118,6 +130,7 @@ def cpp_filename(self): def generate_header_code(self): include_guard = "_IN_CSP_AUTOGEN_" + self._module_name.replace(".", "_").upper() out = f""" +{_GEN_COMMAND} #ifndef {include_guard} #define {include_guard} @@ -140,7 +153,7 @@ def generate_header_code(self): return out def _generate_headers(self): - common_headers = ["csp/core/Exception.h", "csp/engine/Struct.h", "cstddef"] + common_headers = ["csp/core/Exception.h", "csp/core/Platform.h", "csp/engine/Struct.h", "cstddef"] common_headers.extend(self._get_dependent_headers()) return "\n".join(f"#include <{h}>" for h in common_headers) @@ -188,6 +201,8 @@ class CSP_PUBLIC {enum_name} : public csp::CspEnum return out def _generate_struct_class(self, struct_type): + from csp.impl.struct import Struct + struct_name = struct_type.__name__ metainfo = struct_type._metadata_info() @@ -233,6 +248,8 @@ def _generate_struct_class(self, struct_type): mask_offset_assert = ( f"static_assert(( offsetof( {struct_name},{mask_fieldname}) + {mask_byte} ) == {mask_offset} );" ) + field_alignment_assert = f"static_assert( alignof( {ctype} ) == {field_alignment} );" + field_size_assert = f"static_assert( sizeof( {ctype} ) == {field_size} );" # Unfortunately our version of GCC doesnt allow offset of on derived members! if base_struct is not None: @@ -244,9 +261,9 @@ def _generate_struct_class(self, struct_type): ) common_setter = f""" - {field_offset_assert} - static_assert( alignof( {ctype} ) == {field_alignment} ); - static_assert( sizeof( {ctype} ) == {field_size} ); + {"" if self._omit_asserts else field_offset_assert} + {"" if self._omit_asserts else field_alignment_assert} + {"" if self._omit_asserts else field_size_assert} {mask_fieldname}[{mask_byte}] |= {mask_bitmask}; """ @@ -268,9 +285,9 @@ def _generate_struct_class(self, struct_type): getset = f""" const {ctype} & {fieldname}() const {{ - {field_offset_assert} - static_assert( alignof( {ctype} ) == {field_alignment} ); - static_assert( sizeof( {ctype} ) == {field_size} ); + {"" if self._omit_asserts else field_offset_assert} + {"" if self._omit_asserts else field_alignment_assert} + {"" if self._omit_asserts else field_size_assert} if( !{fieldname}_isSet() ) CSP_THROW( csp::ValueError, "field {fieldname} on struct {struct_name} is not set" ); @@ -290,13 +307,13 @@ def _generate_struct_class(self, struct_type): bool {fieldname}_isSet() const {{ - {mask_offset_assert} + {"" if self._omit_asserts else mask_offset_assert} return {mask_fieldname}[{mask_byte}] & {mask_bitmask}; }} void clear_{fieldname}() {{ - {mask_offset_assert} + {"" if self._omit_asserts else mask_offset_assert} {mask_fieldname}[{mask_byte}] &= ~{mask_bitmask}; }} """ @@ -347,13 +364,15 @@ class CSP_PUBLIC {struct_name} : public {base_class} static void assert_mask() {{ - {maskloc_offset_assert} + {"" if self._omit_asserts else maskloc_offset_assert} }} }}; """ return out def generate_cpp_code(self): + from csp.impl.struct import Struct + struct_inits = [] for struct_type in self._struct_types: struct_name = struct_type.__name__ @@ -384,7 +403,7 @@ def generate_cpp_code(self): struct_init = f""" bool {struct_name}::static_init() {{ -{assertions} +{"" if self._omit_asserts else assertions} if( Py_IsInitialized() ) {{ //Note that windows requires we grab the GIL since the windows DLL loading code releases GIL @@ -453,6 +472,7 @@ def generate_cpp_code(self): enum_inits = "\n".join(enum_inits) out = f""" +{_GEN_COMMAND} #include "{self._header_filename}" #include #include @@ -485,17 +505,17 @@ def generate_cpp_code(self): return out -class Test(Struct): - bl: bool - a: int - b: float - s: str - # o: object +# class Test(Struct): +# bl: bool +# a: int +# b: float +# s: str +# # o: object -class Derived(Test): - string: str - flt: float +# class Derived(Test): +# string: str +# flt: float # Test2 = csp.impl.struct.define_struct( 'Test2', { 'A' + str(i) : bool for i in range(25 )}) @@ -522,28 +542,40 @@ class Derived(Test): required=False, help="pass flag to generate types that are imported into the specified module as well", ) + parser.add_argument( + "--omit_asserts", + dest="omit_asserts", + action="store_true", + required=False, + help="pass flag to omit static size assertions from generated code", + ) parser.add_argument( "--dryRun", dest="dry_run", action="store_true", required=False, help="if true write output to stdout" ) args = parser.parse_args() - struct_gen = CodeGenerator(args.module_name, args.outname, args.namespace, bool(args.generate_imports)) - - header_file = os.path.join(args.output_directory, struct_gen.header_filename()) - cpp_file = os.path.join(args.output_directory, struct_gen.cpp_filename()) - - header_code = struct_gen.generate_header_code() - cpp_code = struct_gen.generate_cpp_code() - - if args.dry_run: - print(header_file, ":") - print(header_code) - print(cpp_file) - print(cpp_code) - else: - with open(header_file, "w") as f: - f.write(header_code) - - with open(cpp_file, "w") as f: - f.write(cpp_code) + try: + struct_gen = CodeGenerator( + args.module_name, args.outname, args.namespace, bool(args.generate_imports), bool(args.omit_asserts) + ) + + header_file = os.path.join(args.output_directory, struct_gen.header_filename()) + cpp_file = os.path.join(args.output_directory, struct_gen.cpp_filename()) + + header_code = struct_gen.generate_header_code() + cpp_code = struct_gen.generate_cpp_code() + + if args.dry_run: + print(header_file, ":") + print(header_code) + print(cpp_file) + print(cpp_code) + else: + with open(header_file, "w") as f: + f.write(header_code) + + with open(cpp_file, "w") as f: + f.write(cpp_code) + except Exception: + logging.exception("Error running `csp_autogen`. Your build may fail!") diff --git a/csp/tests/build/test_pregenerated_types.py b/csp/tests/build/test_pregenerated_types.py new file mode 100644 index 000000000..8611574dc --- /dev/null +++ b/csp/tests/build/test_pregenerated_types.py @@ -0,0 +1,66 @@ +import sys +from pathlib import Path +from subprocess import call +from tempfile import TemporaryDirectory + +AUTOGEN_SCRIPT = (Path(__file__).parent / ".." / ".." / "build" / "csp_autogen.py").resolve() + + +class TestPregeneratedTypes: + def test_engine(self): + pregenerated_folder = ( + Path(__file__).parent / ".." / ".." / ".." / "cpp" / "csp" / "engine" / "csp_autogen" + ).resolve() + pregenerated_header = pregenerated_folder / "autogen_types.h" + pregenerated_cpp = pregenerated_folder / "autogen_types.cpp" + + with TemporaryDirectory() as td: + call( + [ + sys.executable, + str(AUTOGEN_SCRIPT), + "-m", + "csp.impl.types.autogen_types", + "-d", + td, + "-o", + "autogen_types", + "--omit_asserts", + ] + ) + generated_header = Path(td) / "autogen_types.h" + generated_cpp = Path(td) / "autogen_types.cpp" + + # Path is different, so skip the lines + # that show the command + assert pregenerated_header.read_text().split("\n")[4:] == generated_header.read_text().split("\n")[4:] + assert pregenerated_cpp.read_text().split("\n")[4:] == generated_cpp.read_text().split("\n")[4:] + + def test_websocket(self): + pregenerated_folder = ( + Path(__file__).parent / ".." / ".." / ".." / "cpp" / "csp" / "adapters" / "websocket" / "csp_autogen" + ).resolve() + pregenerated_header = pregenerated_folder / "websocket_types.h" + pregenerated_cpp = pregenerated_folder / "websocket_types.cpp" + + with TemporaryDirectory() as td: + call( + [ + sys.executable, + str(AUTOGEN_SCRIPT), + "-m", + "csp.adapters.websocket_types", + "-d", + td, + "-o", + "websocket_types", + "--omit_asserts", + ] + ) + generated_header = Path(td) / "websocket_types.h" + generated_cpp = Path(td) / "websocket_types.cpp" + + # Path is different, so skip the lines + # that show the command + assert pregenerated_header.read_text().split("\n")[4:] == generated_header.read_text().split("\n")[4:] + assert pregenerated_cpp.read_text().split("\n")[4:] == generated_cpp.read_text().split("\n")[4:]