Files
odfstudio/codemaker/source/rustmaker/cpproduce.cxx
T
Michael Weghorn 257f798461 rustmaker: Avoid string copy (loplugin:stringview)
Address the following clang plugin error seen in an
--enable-rust-uno build:

    /home/michi/development/git/libreoffice/codemaker/source/rustmaker/cpproduce.cxx:1016:41: error: rather than copy, pass with a view using subView() [loplugin:stringview]
     1016 |                             && (unoType.copy(unoType.lastIndexOf('.') + 1).startsWith("X")));
          |                                 ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Change-Id: Icec8b529abf67ca13243da21136d5f296f53b7d5
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190854
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Tested-by: Jenkins
2025-09-12 13:57:19 +02:00

2738 lines
90 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include "cpproduce.hxx"
#include <rtl/string.hxx>
#include "rustfile.hxx"
#include "type_analyzer.hxx"
#include "cpp_include_manager.hxx"
#include <codemaker/typemanager.hxx>
#include <codemaker/unotype.hxx>
#include <sal/log.hxx>
#include <sal/types.h>
#include <iostream>
#include <filesystem>
#include <string_view>
#include <algorithm>
CppProducer::CppProducer(const OString& outputDir, bool verbose, bool dryRun,
const rtl::Reference<TypeManager>& typeManager)
: m_outputDir(outputDir)
, m_verbose(verbose)
, m_dryRun(dryRun)
, m_typeManager(typeManager)
, m_combinedSourceFile()
, m_combinedHeaderFile()
{
}
void CppProducer::initializeCombinedFile()
{
if (m_dryRun)
return;
// Create the combined source file (.cxx)
m_combinedSourceFile = std::make_unique<CppFile>(m_outputDir, "rust_uno_bindings", ".cxx");
std::filesystem::path sourceFilePath = m_combinedSourceFile->getPath();
if (std::filesystem::exists(sourceFilePath))
{
std::filesystem::remove(sourceFilePath);
}
m_combinedSourceFile->openFile();
// Include the header file
m_combinedSourceFile->beginLine().append("#include \"rust_uno_bindings.hxx\"").endLine();
m_combinedSourceFile->beginLine().append("").endLine();
// Create the combined header file (.hxx)
m_combinedHeaderFile = std::make_unique<CppFile>(m_outputDir, "rust_uno_bindings", ".hxx");
std::filesystem::path headerFilePath = m_combinedHeaderFile->getPath();
if (std::filesystem::exists(headerFilePath))
{
std::filesystem::remove(headerFilePath);
}
m_combinedHeaderFile->openFile();
// Add header guard
m_combinedHeaderFile->beginLine().append("#pragma once").endLine();
m_combinedHeaderFile->beginLine().append("").endLine();
// Add only sal/types.h which definitely exists
m_combinedHeaderFile->beginLine().append("#include <sal/types.h>").endLine();
m_combinedHeaderFile->beginLine().append("").endLine();
}
void CppProducer::finalizeCombinedFile()
{
if (m_combinedSourceFile)
{
m_combinedSourceFile->closeFile();
m_combinedSourceFile.reset();
}
if (m_combinedHeaderFile)
{
m_combinedHeaderFile->closeFile();
m_combinedHeaderFile.reset();
}
}
void CppProducer::produceEnum(std::string_view name,
const rtl::Reference<unoidl::EnumTypeEntity>& entity)
{
if (m_verbose)
std::cout << "[cpp-opaque-enum] " << name << " -> enum bridge functions" << '\n';
generateEnumBridge(name, entity);
}
void CppProducer::produceStruct(std::string_view name,
const rtl::Reference<unoidl::PlainStructTypeEntity>& entity)
{
if (m_verbose)
std::cout << "[cpp-opaque-struct] " << name
<< " -> constructor/destructor + getter/setter functions" << '\n';
generateStructWrapper(name, entity);
// generateStructBridge(name, entity);
}
void CppProducer::produceInterface(std::string_view name,
const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
if (m_verbose)
std::cout << "[cpp-opaque-interface] " << name
<< " -> opaque handle + method wrapper functions" << '\n';
generateInterfaceHeader(name, entity);
generateInterfaceSource(name, entity);
}
void CppProducer::produceService(
std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity)
{
if (m_verbose)
std::cout << "[cpp-opaque-service] " << name << " -> service creation bridge functions"
<< '\n';
generateServiceHeader(name, entity);
generateServiceSource(name, entity);
}
void CppProducer::generateEnumBridge(std::string_view name,
const rtl::Reference<unoidl::EnumTypeEntity>& entity)
{
generateEnumHeader(name, entity);
generateEnumSource(name, entity);
}
void CppProducer::generateStructWrapper(std::string_view name,
const rtl::Reference<unoidl::PlainStructTypeEntity>& entity)
{
generateStructHeader(name, entity);
generateStructSource(name, entity);
}
void CppProducer::generateStructBridge(std::string_view name,
const rtl::Reference<unoidl::PlainStructTypeEntity>&)
{
CppFile file(m_outputDir, name);
if (m_dryRun)
return;
file.openFile();
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Simple opaque struct bridge functions (like old FFI approach)
OString className = getInterfaceClassName(name);
OString functionPrefix = getCFunctionPrefix(name);
file.beginLine()
.append("// Opaque struct creation")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void* ")
.append(functionPrefix)
.append("_create()")
.endLine()
.beginBlock()
.beginLine()
.append("return new ")
.append(className)
.append("();")
.endLine()
.endBlock();
file.beginLine().append("").endLine();
file.endBlock().append(" // extern \"C\"");
file.closeFile();
}
void CppProducer::generateCBridgeIncludes(CppFile& file, std::string_view name,
const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
// Use existing infrastructure for includes
TypeAnalyzer analyzer(m_typeManager);
TypeAnalyzer::TypeInfo typeInfo = analyzer.analyzeInterface(entity);
CppIncludeManager includeManager(typeInfo);
includeManager.dumpIncludes(file, name);
}
void CppProducer::generateCBridgeTypeDefinitions(CppFile& file, std::string_view handleTypeName)
{
// Opaque handle typedef for type safety
file.beginLine()
.append("// Opaque handle for type-safe C FFI")
.endLine()
.beginLine()
.append("typedef void* ")
.append(handleTypeName)
.append(";")
.endLine()
.beginLine()
.append("")
.endLine();
}
void CppProducer::generateCBridgeConstructors(CppFile& file, std::string_view className,
std::string_view functionPrefix,
std::string_view handleTypeName)
{
// Opaque constructor function - creates interface instance from existing Reference
file.beginLine()
.append("// Opaque constructor - creates interface wrapper from UNO Reference")
.endLine()
.beginLine()
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_create_from_reference(void* xInterface)")
.endLine()
.beginBlock()
.beginLine()
.append("if (!xInterface) return nullptr;")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Cast void* back to Reference<")
.append(className)
.append(">")
.endLine()
.beginLine()
.append("Reference<")
.append(className)
.append(">* pRef = static_cast<Reference<")
.append(className)
.append(">*>(xInterface);")
.endLine()
.beginLine()
.append("if (!pRef->is()) return nullptr;")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Create wrapper with interface reference")
.endLine()
.beginLine()
.append("return new ")
.append(className)
.append("Wrapper(*pRef);")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Opaque service creation - attempts to create UNO service")
.endLine()
.beginLine()
.append("// Note: This is a simplified approach - real service creation")
.endLine()
.beginLine()
.append("// would need ComponentContext and service name resolution")
.endLine()
.beginLine()
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_create_service(void* xContext, const char* serviceName)")
.endLine()
.beginBlock()
.beginLine()
.append("if (!xContext || !serviceName) return nullptr;")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("try")
.endLine()
.beginBlock()
.beginLine()
.append("// Cast context")
.endLine()
.beginLine()
.append("Reference<XComponentContext>* pContext =")
.endLine()
.extraIndent()
.beginLine()
.append("static_cast<Reference<XComponentContext>*>(xContext);")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Create service")
.endLine()
.beginLine()
.append("OUString svcName = OUString::createFromAscii(serviceName);")
.endLine()
.beginLine()
.append("Reference<")
.append(className)
.append("> xService(")
.endLine()
.extraIndent()
.beginLine()
.append("pContext->getServiceManager()->createInstanceWithContext(")
.endLine()
.extraIndent()
.beginLine()
.append("svcName, *pContext), UNO_QUERY);")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!xService.is()) return nullptr;")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Return wrapper")
.endLine()
.beginLine()
.append("return new ")
.append(className)
.append("Wrapper(xService);")
.endLine()
.endBlock()
.beginLine()
.append("catch (const Exception& ex)")
.endLine()
.beginBlock()
.beginLine()
.append("// TODO: Log exception details")
.endLine()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"UNO exception in interface method\");")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.endBlock();
}
void CppProducer::generateCBridgeDestructor(CppFile& file, std::string_view className,
std::string_view functionPrefix,
std::string_view handleTypeName)
{
// Opaque destructor function - destroys interface instance
file.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Opaque destructor - destroys interface wrapper")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_destroy(")
.append(handleTypeName)
.append(" handle)")
.endLine()
.beginBlock()
.beginLine()
.append("if (handle)")
.endLine()
.beginBlock()
.beginLine()
.append("delete static_cast<")
.append(className)
.append("Wrapper*>(handle);")
.endLine()
.endBlock();
}
void CppProducer::generateCBridgeValidation(CppFile& file, std::string_view className,
std::string_view functionPrefix,
std::string_view handleTypeName)
{
// Opaque validity check function
file.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Opaque validity check - tests if interface is valid")
.endLine()
.beginLine()
.append("bool ")
.append(functionPrefix)
.append("_is_valid(")
.append(handleTypeName)
.append(" handle)")
.endLine()
.beginBlock()
.beginLine()
.append("if (!handle) return false;")
.endLine()
.beginLine()
.append("return static_cast<")
.append(className)
.append("Wrapper*>(handle)->isValid();")
.endLine()
.endBlock();
}
void CppProducer::generateCBridgeMethods(CppFile& file, std::string_view className,
std::string_view functionPrefix,
std::string_view handleTypeName,
const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
// Opaque method bridge functions - each method gets unique extern "C" function
for (const auto& method : entity->getDirectMethods())
{
file.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Opaque method bridge for ")
.append(method.name)
.endLine()
.beginLine()
.append(getMethodReturnType(method.returnType))
.append(" ")
.append(functionPrefix)
.append("_")
.append(method.name)
.append("(")
.append(handleTypeName)
.append(" handle");
// Use proper types instead of void* for parameters
for (const auto& param : method.parameters)
{
file.append(", ").append(getCppTypeName(param.type)).append(" ").append(param.name);
}
file.append(")")
.endLine()
.beginBlock()
.beginLine()
.append("if (!handle) return ")
.append(getMethodDefaultReturn(method.returnType))
.append(";")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("auto* wrapper = static_cast<")
.append(className)
.append("Wrapper*>(handle);")
.endLine()
.beginLine()
.append("return wrapper->")
.append(method.name)
.append("(");
// Pass through all opaque parameters
bool first = true;
for (const auto& param : method.parameters)
{
if (!first)
file.append(", ");
first = false;
file.append(param.name);
}
file.append(");").endLine().endBlock();
}
}
void CppProducer::generateCBridge(std::string_view name,
const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
CppFile file(m_outputDir, name);
if (m_dryRun)
return;
file.openFile();
// Generate file setup and includes
generateCBridgeIncludes(file, name, entity);
// Generate type definitions
OString className = getInterfaceClassName(name);
OString functionPrefix = getCFunctionPrefix(name);
OString handleTypeName = functionPrefix + "Handle";
generateCBridgeTypeDefinitions(file, handleTypeName);
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Generate constructor functions
generateCBridgeConstructors(file, className, functionPrefix, handleTypeName);
// Generate destructor function
generateCBridgeDestructor(file, className, functionPrefix, handleTypeName);
// Generate validation function
generateCBridgeValidation(file, className, functionPrefix, handleTypeName);
// Generate method bridge functions
generateCBridgeMethods(file, className, functionPrefix, handleTypeName, entity);
file.beginLine().append("").endLine();
file.endBlock().append(" // extern \"C\"");
file.closeFile();
}
void CppProducer::generateStructHeader(std::string_view name,
const rtl::Reference<unoidl::PlainStructTypeEntity>& entity)
{
if (m_dryRun || !m_combinedHeaderFile)
return;
// Use combined file instead of individual file
CppFile& file = *m_combinedHeaderFile;
file.openFileAppend();
// Generate includes following the pattern used in generateStructSource
file.beginLine().append("#include <sal/types.h>").endLine();
file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine();
file.beginLine().append("#include <com/sun/star/uno/Any.hxx>").endLine();
// Include the struct definition
std::string headerName(name);
std::replace(headerName.begin(), headerName.end(), '.', '/');
file.beginLine().append("#include <").append(headerName).append(".hpp>").endLine();
file.beginLine().append("").endLine();
file.beginLine().append("using namespace com::sun::star::uno;").endLine();
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Add struct-specific namespace
std::string cppNamespace(name);
size_t lastDot = cppNamespace.rfind('.');
if (lastDot != std::string::npos)
{
cppNamespace = cppNamespace.substr(0, lastDot);
// Convert dots to double colons for C++ namespace syntax
size_t pos = 0;
while ((pos = cppNamespace.find('.', pos)) != std::string::npos)
{
cppNamespace.replace(pos, 1, "::");
pos += 2;
}
}
// Simple opaque struct bridge functions
OString functionPrefix = getCFunctionPrefix(name);
OString handleTypeName = functionPrefix + "Handle";
file.beginLine()
.append("// Opaque handle for type-safe C FFI")
.endLine()
.beginLine()
.append("typedef void* ")
.append(handleTypeName)
.append(";")
.endLine()
.beginLine()
.append("")
.endLine();
file.beginLine()
.append("// Opaque struct creation")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_constructor();")
.endLine();
file.beginLine()
.append("// Opaque handle destruction")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_destroy(")
.append(handleTypeName)
.append(" handle);")
.endLine();
file.beginLine()
.append("// Struct from_ptr type casting")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_from_ptr(void* ptr);")
.endLine();
file.beginLine()
.append("")
.endLine()
.beginLine()
.append("// Getters/setters for direct struct members")
.endLine();
// Opaque member access functions - each member gets getter/setter
for (const auto& member : entity->getDirectMembers())
{
file.beginLine()
.append("// Opaque getter for ")
.append(member.name)
.append(" (type: ")
.append(member.type)
.append(")")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void* ")
.append(functionPrefix)
.append("_get_")
.append(member.name)
.append("(")
.append(handleTypeName)
.append(" handle);")
.endLine();
file.beginLine()
.append("// Opaque setter for ")
.append(member.name)
.append(" (type: ")
.append(member.type)
.append(")")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_set_")
.append(member.name)
.append("(")
.append(handleTypeName)
.append(" handle, void* value);")
.endLine();
}
file.endBlock().append("// extern \"C\"").endLine();
file.closeFile();
}
// Unified include generation function to eliminate duplication
void CppProducer::generateCommonIncludes(CppFile& file, std::string_view name, bool needsLogging,
bool needsUnoTypes)
{
// Always include sal/types.h
file.beginLine().append("#include <sal/types.h>").endLine();
// Include logging if needed
if (needsLogging)
{
file.beginLine().append("#include <sal/log.hxx>").endLine();
}
// Include UNO types if needed
if (needsUnoTypes)
{
file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine();
file.beginLine().append("#include <com/sun/star/uno/Any.hxx>").endLine();
}
// Include the type-specific header
std::string headerName(name);
std::replace(headerName.begin(), headerName.end(), '.', '/');
file.beginLine().append("#include <").append(headerName).append(".hpp>").endLine();
file.beginLine().append("").endLine();
}
void CppProducer::generateStructSourceIncludes(CppFile& file, std::string_view name)
{
generateCommonIncludes(file, name, false, true);
}
void CppProducer::generateEnumIncludes(CppFile& file, std::string_view name)
{
generateCommonIncludes(file, name, false, false);
}
void CppProducer::generateEnumSourceIncludes(CppFile& file, std::string_view name)
{
generateCommonIncludes(file, name, true, false);
}
void CppProducer::generateEnumHeader(std::string_view name,
const rtl::Reference<unoidl::EnumTypeEntity>&)
{
if (m_dryRun || !m_combinedHeaderFile)
return;
// Use combined file instead of individual file
CppFile& file = *m_combinedHeaderFile;
file.openFileAppend();
// Add separator comment for this enum
file.beginLine().append("").endLine();
file.beginLine().append("// === ").append(name).append(" ===").endLine();
// Generate includes in combined file
generateEnumIncludes(file, name);
// Generate extern "C" declarations
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Opaque enum conversion functions - treat enums as opaque values
OString enumName = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name)));
OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming
file.beginLine()
.append(" SAL_DLLPUBLIC_EXPORT ")
.append(enumName)
.append(" ")
.append(functionPrefix)
.append("_from_i32(sal_Int32 value);")
.endLine();
file.beginLine()
.append(" SAL_DLLPUBLIC_EXPORT sal_Int32 ")
.append(functionPrefix)
.append("_to_i32(")
.append(enumName)
.append(" value);")
.endLine();
file.beginLine().append("}").endLine();
file.closeFile();
}
void CppProducer::generateEnumSource(std::string_view name,
const rtl::Reference<unoidl::EnumTypeEntity>&)
{
if (m_dryRun || !m_combinedSourceFile)
return;
// Use combined file instead of individual file
CppFile& file = *m_combinedSourceFile;
file.openFileAppend();
// Add separator comment for this enum
file.beginLine().append("").endLine();
file.beginLine().append("// === ").append(name).append(" ===").endLine();
// Generate includes for source file
generateEnumSourceIncludes(file, name);
// Generate extern "C" implementations
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Opaque enum conversion functions implementations
OString enumName = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name)));
OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming
// from_i32 implementation
file.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(enumName)
.append(" ")
.append(functionPrefix)
.append("_from_i32(sal_Int32 value)")
.endLine()
.beginBlock()
.beginLine()
.append("return static_cast<")
.append(enumName)
.append(">(value);")
.endLine()
.endBlock();
// to_i32 implementation
file.beginLine()
.append("")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT sal_Int32 ")
.append(functionPrefix)
.append("_to_i32(")
.append(enumName)
.append(" value)")
.endLine()
.beginBlock()
.beginLine()
.append("return static_cast<sal_Int32>(value);")
.endLine()
.endBlock();
file.beginLine().append("}").endLine();
file.closeFile();
}
// Unified namespace generation function to eliminate duplication
void CppProducer::generateSourceNamespaces(CppFile& file, std::string_view name)
{
file.beginLine().append("using namespace com::sun::star::uno;").endLine();
// Add type-specific namespace
std::string cppNamespace(name);
size_t lastDot = cppNamespace.rfind('.');
if (lastDot != std::string::npos)
{
cppNamespace = cppNamespace.substr(0, lastDot);
// Convert dots to double colons for C++ namespace syntax
size_t pos = 0;
while ((pos = cppNamespace.find('.', pos)) != std::string::npos)
{
cppNamespace.replace(pos, 1, "::");
pos += 2;
}
file.beginLine().append("using namespace ").append(cppNamespace).append(";").endLine();
}
file.beginLine().append("").endLine();
}
void CppProducer::generateStructSourceNamespaces(CppFile& file, std::string_view name)
{
generateSourceNamespaces(file, name);
}
void CppProducer::generateStructSourceBasicFunctions(CppFile& file, std::string_view functionPrefix,
std::string_view handleTypeName,
std::string_view className)
{
// Generate struct creation function
file.beginLine()
.append("// Opaque struct creation")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_constructor()")
.endLine()
.beginBlock()
.beginLine()
.append("return new ")
.append(className)
.append("();")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine();
// Generate struct destroy function
file.beginLine()
.append("// Opaque handle destruction")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_destroy(")
.append(handleTypeName)
.append(" handle)")
.endLine()
.beginBlock()
.beginLine()
.append("delete static_cast<")
.append(className)
.append("*>(handle);")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine();
// Generate from_ptr function for struct type casting
file.beginLine()
.append("// Struct from_ptr type casting")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_from_ptr(void* ptr)")
.endLine()
.beginBlock()
.beginLine()
.append("if (ptr == nullptr) return nullptr;")
.endLine()
.beginLine()
.append("// For structs, we assume the pointer is already the correct type")
.endLine()
.beginLine()
.append("// Create a copy of the struct")
.endLine()
.beginLine()
.append("")
.append(className)
.append("* source = static_cast<")
.append(className)
.append("*>(ptr);")
.endLine()
.beginLine()
.append("return new ")
.append(className)
.append("(*source);")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine();
}
void CppProducer::generateStructSourceBaseMembers(
CppFile& file, std::string_view functionPrefix, std::string_view handleTypeName,
std::string_view className, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity)
{
// Generate getters/setters for base type members (if struct has a base)
if (!entity->getDirectBase().isEmpty())
{
file.beginLine()
.append("// Getters/setters for base type members (")
.append(entity->getDirectBase())
.append(")")
.endLine();
// Get base type entity to access its members
rtl::Reference<unoidl::Entity> baseEntity;
codemaker::UnoType::Sort baseSort
= m_typeManager->getSort(entity->getDirectBase(), &baseEntity);
if (baseSort == codemaker::UnoType::Sort::PlainStruct)
{
rtl::Reference<unoidl::PlainStructTypeEntity> baseStruct(
static_cast<unoidl::PlainStructTypeEntity*>(baseEntity.get()));
for (const auto& baseMember : baseStruct->getDirectMembers())
{
generateStructMemberAccessors(file, functionPrefix, handleTypeName, className,
baseMember);
}
}
}
}
void CppProducer::generateStructSourceDirectMembers(
CppFile& file, std::string_view functionPrefix, std::string_view handleTypeName,
std::string_view className, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity)
{
// Generate getters/setters for direct struct members
file.beginLine().append("// Getters/setters for direct struct members").endLine();
for (const auto& member : entity->getDirectMembers())
{
generateStructMemberAccessors(file, functionPrefix, handleTypeName, className, member);
}
}
void CppProducer::generateStructMemberAccessors(CppFile& file, std::string_view functionPrefix,
std::string_view handleTypeName,
std::string_view className,
const unoidl::PlainStructTypeEntity::Member& member)
{
// Generate getter implementation
file.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void* ")
.append(functionPrefix)
.append("_get_")
.append(member.name)
.append("(")
.append(handleTypeName)
.append(" handle)")
.endLine()
.beginBlock()
.beginLine()
.append(className)
.append("* obj = static_cast<")
.append(className)
.append("*>(handle);")
.endLine()
.beginLine()
.append("return &(obj->")
.append(member.name)
.append(");")
.endLine()
.endBlock();
// Generate setter implementation
file.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_set_")
.append(member.name)
.append("(")
.append(handleTypeName)
.append(" handle, void* value)")
.endLine()
.beginBlock()
.beginLine()
.append(className)
.append("* obj = static_cast<")
.append(className)
.append("*>(handle);")
.endLine()
.beginLine()
.append("obj->")
.append(member.name);
// Check if this is a UNO interface type by checking the UNO type string
OUString unoType = member.type;
SAL_INFO("codemaker", "Struct member " << member.name << " UNO type: " << unoType);
// UNO interface types start with "com.sun.star" and end with an interface name (typically starting with "X")
// Examples: "com.sun.star.uno.XInterface", "com.sun.star.text.XText"
bool isInterfaceType = (unoType.startsWith("com.sun.star.") && (unoType.lastIndexOf('.') != -1)
&& (unoType.subView(unoType.lastIndexOf('.') + 1).starts_with('X')));
if (isInterfaceType)
{
SAL_INFO("codemaker", "Detected UNO interface type, using pointer assignment");
// For interface types, assign the pointer directly without dereferencing
// The C++ struct expects Reference<Interface>, but we assign the raw interface pointer
file.append(" = reinterpret_cast<")
.append(convertUnoTypeToCpp(unoType))
.append("*>(value);");
}
else
{
SAL_INFO("codemaker", "Non-interface type, using dereferenced assignment");
// For non-interface types, dereference the pointer as before
file.append(" = *reinterpret_cast<const ")
.append(convertUnoTypeToCpp(unoType))
.append("*>(value);");
}
file.endLine().endBlock().beginLine().append("").endLine();
}
void CppProducer::generateStructSource(std::string_view name,
const rtl::Reference<unoidl::PlainStructTypeEntity>& entity)
{
if (m_dryRun || !m_combinedSourceFile)
return;
// Use combined file instead of individual file
CppFile& file = *m_combinedSourceFile;
file.openFileAppend();
// Add separator comment for this struct
file.beginLine().append("").endLine();
file.beginLine().append("// === ").append(name).append(" ===").endLine();
// Start isolated block for this struct to avoid namespace conflicts
file.beginLine().append("namespace");
file.beginBlock();
// Generate includes
generateStructSourceIncludes(file, name);
// Generate namespaces (now in isolated scope)
generateStructSourceNamespaces(file, name);
OString functionPrefix = getCFunctionPrefix(name);
OString handleTypeName = functionPrefix + "Handle";
// Use fully qualified C++ type name to avoid namespace ambiguity
OString className = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name)));
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Generate basic functions (creation/destruction)
generateStructSourceBasicFunctions(file, functionPrefix, handleTypeName, className);
// Generate accessors for base type members
generateStructSourceBaseMembers(file, functionPrefix, handleTypeName, className, entity);
// Generate accessors for direct members
generateStructSourceDirectMembers(file, functionPrefix, handleTypeName, className, entity);
file.endBlock().append(" // extern \"C\"").endLine();
// End isolated block for this struct
file.endBlock().append(" // End isolated block for ").append(name).endLine();
file.closeFile();
}
// Helper functions
std::string_view CppProducer::splitName(std::string_view name)
{
size_t split = name.find_last_of(".::");
if (split != std::string_view::npos)
return name.substr(split + 1);
else
return name;
}
OString CppProducer::getInterfaceClassName(std::string_view unoName)
{
return OString(splitName(unoName));
}
OString CppProducer::getCFunctionPrefix(std::string_view unoName)
{
// Convert com.sun.star.lang.XMain to com__sun__star__lang__XMain for function names
OString temp(unoName);
return temp.replaceAll("."_ostr, "__"_ostr);
}
OString CppProducer::getMethodReturnType(std::u16string_view returnType) const
{
// Use the sophisticated type mapping from the old FFI producer
return getCppTypeName(returnType);
}
OString CppProducer::getMethodDefaultReturn(std::u16string_view returnType) const
{
// First resolve any typedefs to their underlying types
OUString resolvedType = resolveTypedef(returnType);
// Handle void vs non-void return types properly
if (resolvedType == u"void")
return ""_ostr; // void methods don't return anything
// Return appropriate default values for different types
if (resolvedType == u"double")
return "0.0"_ostr;
else if (resolvedType == u"float")
return "0.0f"_ostr;
else if (resolvedType == u"sal_Int32" || resolvedType == u"long" || resolvedType == u"sal_Int16"
|| resolvedType == u"short" || resolvedType == u"sal_Int8" || resolvedType == u"byte"
|| resolvedType == u"sal_uInt8" || resolvedType == u"sal_uInt16"
|| resolvedType == u"sal_uInt32" || resolvedType == u"sal_Int64"
|| resolvedType == u"sal_uInt64" || resolvedType == u"sal_Unicode"
|| resolvedType == u"unsigned short" || resolvedType == u"unsigned long"
|| resolvedType == u"unsigned hyper" || resolvedType == u"hyper")
return "0"_ostr;
else if (resolvedType == u"sal_Bool" || resolvedType == u"boolean")
return "false"_ostr;
else
{
// Check if it's an enum type
if (isUnoType(resolvedType) && isUnoEnum(resolvedType))
{
// Return default enum value (first enum value, typically 0)
OString returnTypeStr = u2b(resolvedType);
size_t lastDot = returnTypeStr.lastIndexOf('.');
std::string_view enumName = (lastDot != std::string_view::npos)
? returnTypeStr.subView(lastDot + 1)
: returnTypeStr;
return "static_cast<"_ostr + enumName + "_>(0)"_ostr;
}
// Check if it's a struct type
else if (isUnoType(resolvedType) && isUnoStruct(resolvedType))
{
// Return empty struct for struct types
return "{}"_ostr; // Empty struct literal
}
else
{
return "nullptr"_ostr; // For pointer/interface/any types
}
}
}
OString CppProducer::convertUnoTypeToCpp(std::u16string_view unoType) const
{
std::string result(unoType.begin(), unoType.end());
// Handle sequences: []type -> Sequence<type>
int sequenceDepth = 0;
while (result.starts_with("[]"))
{
result = result.substr(2);
sequenceDepth++;
}
// Check if the inner type is an interface and wrap with Reference<> if needed for sequences
bool needsReferenceWrapper = false;
if (sequenceDepth > 0 && result.find(".") != std::string::npos)
{
// For UNO types with namespaces, check if it's an interface (before namespace conversion)
rtl::Reference<unoidl::Entity> entity;
OUString unoTypeName = OUString::createFromAscii(result);
codemaker::UnoType::Sort sort = m_typeManager->getSort(unoTypeName, &entity);
if (sort == codemaker::UnoType::Sort::Interface)
{
needsReferenceWrapper = true;
}
}
// Handle template arguments recursively (before namespace conversion)
result = convertTemplateArguments(result);
// Convert dots to double colons for C++ namespace syntax
size_t pos = 0;
while ((pos = result.find('.', pos)) != std::string::npos)
{
result.replace(pos, 1, "::");
pos += 2;
}
// Handle primitive types (only for non-template contexts)
if (result.find('<') == std::string::npos)
{
if (result == "boolean")
result = "sal_Bool";
else if (result == "byte")
result = "sal_Int8";
else if (result == "short")
result = "sal_Int16";
else if (result == "unsigned short")
result = "sal_uInt16";
else if (result == "long")
result = "sal_Int32";
else if (result == "unsigned long")
result = "sal_uInt32";
else if (result == "hyper")
result = "sal_Int64";
else if (result == "unsigned hyper")
result = "sal_uInt64";
else if (result == "float")
result = "float";
else if (result == "double")
result = "double";
else if (result == "char")
result = "sal_Unicode";
else if (result == "string")
result = "OUString";
else if (result == "type")
result = "Type";
else if (result == "any")
result = "Any";
// else: keep as-is for UNO types like com::sun::star::...
}
// Apply Reference<> wrapper if needed
if (needsReferenceWrapper)
{
result = "Reference<" + result + ">";
}
// Wrap with Sequence<> for each nesting level
for (int i = 0; i < sequenceDepth; i++)
{
result = "Sequence<" + result + ">";
}
return OString(result.c_str());
}
std::string CppProducer::convertTemplateArguments(const std::string& unoType) const
{
std::string result = unoType;
// Find template arguments within < >
size_t start = result.find('<');
if (start == std::string::npos)
return result; // No template arguments
size_t end = result.rfind('>');
if (end == std::string::npos || end <= start)
return result; // Malformed template
// Extract and process template arguments
std::string prefix = result.substr(0, start + 1);
std::string suffix = result.substr(end);
std::string args = result.substr(start + 1, end - start - 1);
// Split arguments by comma (simple split for now)
std::string processedArgs = "";
size_t argStart = 0;
size_t comma = 0;
while (true)
{
comma = args.find(',', argStart);
std::string arg;
if (comma == std::string::npos)
{
arg = args.substr(argStart);
}
else
{
arg = args.substr(argStart, comma - argStart);
}
// Trim whitespace
arg.erase(0, arg.find_first_not_of(" \t"));
arg.erase(arg.find_last_not_of(" \t") + 1);
// Recursively process nested template arguments first
arg = convertTemplateArguments(arg);
// Handle sequence types first: []type -> Sequence<type>
if (arg.starts_with("[]"))
{
std::string innerType = arg.substr(2);
arg = "Sequence<" + innerType + ">";
}
// Convert basic types
if (arg == "string")
arg = "OUString";
else if (arg == "boolean")
arg = "sal_Bool";
else if (arg == "byte")
arg = "sal_Int8";
else if (arg == "short")
arg = "sal_Int16";
else if (arg == "long")
arg = "sal_Int32";
else if (arg == "hyper")
arg = "sal_Int64";
else if (arg == "float")
arg = "float";
else if (arg == "double")
arg = "double";
else if (arg == "char")
arg = "sal_Unicode";
else if (arg == "type")
arg = "Type";
else if (arg == "any")
arg = "Any";
// For complex types, convert dots to ::
else if (arg.find('.') != std::string::npos)
{
size_t pos = 0;
while ((pos = arg.find('.', pos)) != std::string::npos)
{
arg.replace(pos, 1, "::");
pos += 2;
}
}
if (!processedArgs.empty())
processedArgs += ",";
processedArgs += arg;
if (comma == std::string::npos)
break;
argStart = comma + 1;
}
return prefix + processedArgs + suffix;
}
void CppProducer::generateInterfaceHeader(std::string_view name,
const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
if (m_dryRun || !m_combinedHeaderFile)
return;
// Use combined file instead of individual file
CppFile& file = *m_combinedHeaderFile;
file.openFileAppend();
// Add separator comment for this interface
file.beginLine().append("").endLine();
file.beginLine().append("// === ").append(name).append(" ===").endLine();
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Generate opaque handle typedef
OString functionPrefix = getCFunctionPrefix(name);
OString handleTypeName = functionPrefix + "Handle";
file.beginLine()
.append("// Opaque handle for type-safe C FFI")
.endLine()
.beginLine()
.append("typedef void* ")
.append(handleTypeName)
.append(";")
.endLine()
.beginLine()
.append("")
.endLine();
// Generate create/destroy functions
file.beginLine()
.append("// Interface lifecycle")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_constructor();")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_destructor(")
.append(handleTypeName)
.append(" handle);")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_from_ptr(void* ptr);")
.endLine();
file.beginLine().append("").endLine();
// Generate method wrappers for all methods
file.beginLine().append("// Method wrappers").endLine();
// TODO: Add interface inheritance support in the future
// For now, only process direct methods from this interface
// Process only this interface's direct methods (no inheritance)
for (const auto& method : entity->getDirectMethods())
{
// Generate method signature
file.beginLine();
// Return type - use void* for all non-void returns (keep it simple)
OString returnType = getMethodReturnType(method.returnType);
if (returnType == "void")
{
file.append("SAL_DLLPUBLIC_EXPORT void ");
}
else
{
file.append("SAL_DLLPUBLIC_EXPORT void* ");
}
// Function name: com__sun__star__lang__XMain__methodName
file.append(functionPrefix)
.append("_")
.append(method.name)
.append("(")
.append(handleTypeName)
.append(" handle");
// Add parameters
for (const auto& param : method.parameters)
{
file.append(", void* ").append(param.name);
}
file.append(");");
file.endLine();
}
file.endBlock().beginLine().append("// extern \"C\"").endLine();
file.closeFile();
}
void CppProducer::generateInterfaceSourceIncludes(CppFile& file, std::string_view name)
{
generateCommonIncludes(file, name, true, true);
}
void CppProducer::generateInterfaceSourceNamespaces(CppFile& file, std::string_view name)
{
generateSourceNamespaces(file, name);
}
void CppProducer::generateInterfaceSourceBasicFunctions(CppFile& file,
std::string_view functionPrefix,
std::string_view className,
std::string_view handleTypeName)
{
// Interface creation
file.beginLine()
.append("// Interface creation - creates empty Reference (to be populated by Rust)")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_constructor()")
.endLine()
.beginBlock()
.beginLine()
.append("//Debug: Interface constructor called for ")
.append(className)
.endLine()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"Debug: Creating interface ")
.append(className)
.append("\");")
.endLine()
.beginLine()
.append("// Create empty Reference - actual interface will be set from Rust side")
.endLine()
.beginLine()
.append("auto res = new Reference<")
.append(className)
.append(">();")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!res)")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rust_uno_ffi\", \"Failed to allocate Reference<")
.append(className)
.append("> wrapper\");")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine()
.beginLine()
.append(
"// Note: Empty Reference is expected here - is() will be false until set from Rust")
.endLine()
.beginLine()
.append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created empty Reference<")
.append(className)
.append("> wrapper\");")
.endLine()
.beginLine()
.append("return res;")
.endLine()
.endBlock();
file.beginLine().append("").endLine();
// Interface destruction
file.beginLine()
.append("// Interface destruction")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void ")
.append(functionPrefix)
.append("_destructor(")
.append(handleTypeName)
.append(" handle)")
.endLine()
.beginBlock()
.beginLine()
.append("delete static_cast<Reference<")
.append(className)
.append(">*>(handle);")
.endLine()
.endBlock();
file.beginLine().append("").endLine();
// Interface casting from void* to typed Reference using UNO_QUERY
file.beginLine()
.append("// Interface casting - casts void* to typed Reference using UNO_QUERY")
.endLine()
.beginLine()
.append("SAL_DLLPUBLIC_EXPORT ")
.append(handleTypeName)
.append(" ")
.append(functionPrefix)
.append("_from_ptr(void* ptr)")
.endLine()
.beginBlock()
.beginLine()
.append("if (ptr == nullptr) return nullptr;")
.endLine()
.beginLine()
.append("try")
.endLine()
.beginBlock()
.beginLine()
.append("// Convert XInterface* to ")
.append(className)
.append(" via UNO_QUERY")
.endLine()
.beginLine()
.append("Reference<XInterface>* interfacePtr = static_cast<Reference<XInterface>*>(ptr);")
.endLine()
.beginLine()
.append("Reference<")
.append(className)
.append("> queryResult(interfacePtr->get(), UNO_QUERY);")
.endLine()
.beginLine()
.append("if (!queryResult.is()) return nullptr;")
.endLine()
.beginLine()
.append("Reference<")
.append(className)
.append(">* refPtr = new Reference<")
.append(className)
.append(">(queryResult);")
.endLine()
.beginLine()
.append("if (refPtr && refPtr->is())")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_INFO(\"rust_uno_ffi\", \"Successfully created Reference<")
.append(className)
.append("> in from_ptr\");")
.endLine()
.beginLine()
.append("return refPtr;")
.endBlock()
.beginLine()
.append("SAL_WARN(\"rust_uno_ffi\", \"Reference is invalid in from_ptr for ")
.append(className)
.append("\");")
.endLine()
.beginLine()
.append("delete refPtr;")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.beginLine()
.append("catch (const Exception& e)")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"UNO exception in ")
.append(functionPrefix)
.append("_from_ptr: \" << e.Message);")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.beginLine()
.append("}")
.endLine()
.endBlock();
file.beginLine().append("").endLine();
}
void CppProducer::generateInterfaceSourceMethodImplementations(
CppFile& file, std::string_view functionPrefix, std::string_view className,
std::string_view handleTypeName, const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
// Generate method implementations
file.beginLine().append("// Method implementations").endLine();
// TODO: Add interface inheritance support in the future
// For now, only process direct methods from this interface (no inheritance)
// Process only this interface's direct methods
for (const auto& method : entity->getDirectMethods())
{
generateSingleInterfaceMethod(file, functionPrefix, className, handleTypeName, method);
}
}
void CppProducer::generateSingleInterfaceMethod(CppFile& file, std::string_view functionPrefix,
std::string_view className,
std::string_view handleTypeName,
const unoidl::InterfaceTypeEntity::Method& method)
{
file.beginLine();
// Use void* for all non-void returns (keep it simple)
OString returnType = getMethodReturnType(method.returnType);
if (returnType == "void")
{
file.append("SAL_DLLPUBLIC_EXPORT void ");
}
else
{
file.append("SAL_DLLPUBLIC_EXPORT void* ");
}
file.append(functionPrefix)
.append("_")
.append(method.name)
.append("(")
.append(handleTypeName)
.append(" handle");
// Add parameters
for (const auto& param : method.parameters)
{
file.append(", void* ").append(param.name);
}
file.append(")").endLine().beginBlock();
// Implementation body
file.beginLine()
.append("//Debug: Method ")
.append(method.name)
.append(" called")
.endLine()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"Debug: Method ")
.append(className)
.append("::")
.append(method.name)
.append(" called\");")
.endLine()
.beginLine()
.append("Reference<")
.append(className)
.append(">* ref = static_cast<Reference<")
.append(className)
.append(">*>(handle);")
.endLine()
.beginLine()
.append("//Debug: Checking reference validity")
.endLine()
.beginLine()
.append("if (!ref) { SAL_WARN(\"rustmaker\", \"Debug: ref is null\"); ")
.append(method.returnType == u"void" ? "return;" : "return nullptr;")
.append(" }")
.endLine()
.beginLine()
.append("if (!ref->is()) { SAL_WARN(\"rustmaker\", \"Debug: ref->is() is false\"); ")
.append(method.returnType == u"void" ? "return;" : "return nullptr;")
.append(" }")
.endLine();
// Generate try-catch block for UNO exceptions
file.beginLine().append("try").endLine().beginBlock();
// Generate actual UNO method call
generateActualMethodCall(file, method);
// Note: Return value conversion is now handled inside generateActualMethodCall()
// Exception handling
file.endBlock()
.beginLine()
.append("catch (const Exception& ex)")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"UNO exception in ")
.append(className)
.append("_")
.append(method.name)
.append(": \" << ex.Message);")
.endLine();
// Return appropriate error value based on return type
if (method.returnType == u"void")
{
file.beginLine().append("return; // void method - no return on exception").endLine();
}
else
{
file.beginLine().append("return nullptr; // error return for non-void method").endLine();
}
file.endBlock().endBlock();
file.beginLine().append("").endLine();
}
void CppProducer::generateInterfaceSource(std::string_view name,
const rtl::Reference<unoidl::InterfaceTypeEntity>& entity)
{
if (m_dryRun || !m_combinedSourceFile)
return;
// Use combined file instead of individual file
CppFile& file = *m_combinedSourceFile;
file.openFileAppend();
// Add separator comment for this interface
file.beginLine().append("").endLine();
file.beginLine().append("// === ").append(name).append(" ===").endLine();
// Generate includes
generateInterfaceSourceIncludes(file, name);
// Add namespaces
generateInterfaceSourceNamespaces(file, name);
file.beginLine().append("extern \"C\"").endLine().beginBlock();
// Generate implementation
OString functionPrefix = getCFunctionPrefix(name);
OString handleTypeName = functionPrefix + "Handle";
// Use fully qualified C++ type name to avoid namespace ambiguity
OString className = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name)));
// Generate basic functions (creation/destruction)
generateInterfaceSourceBasicFunctions(file, functionPrefix, className, handleTypeName);
// Generate method implementations
generateInterfaceSourceMethodImplementations(file, functionPrefix, className, handleTypeName,
entity);
file.endBlock().append(" // extern \"C\"");
file.beginLine().append("").endLine();
file.closeFile();
}
void CppProducer::generateActualMethodCall(CppFile& file,
const unoidl::InterfaceTypeEntity::Method& method)
{
// Generate actual UNO method call based on method signature
file.beginLine()
.append("//Debug: About to call UNO method ")
.append(method.name)
.endLine()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"Debug: Calling UNO method ")
.append(method.name)
.append(" with ")
.append(OString::number(static_cast<sal_Int32>(method.parameters.size())))
.append(" parameters\");")
.endLine();
if (method.returnType != u"void")
{
// Method with return value
file.beginLine()
.append("// Call actual UNO method with return value")
.endLine()
.beginLine();
file.append("auto result = (*ref)->").append(method.name).append("(");
}
else
{
// Void method
file.beginLine().append("// Call actual UNO method (void return)").endLine().beginLine();
file.append("(*ref)->").append(method.name).append("(");
}
// Add parameters with proper type conversion
bool first = true;
for (const auto& param : method.parameters)
{
if (!first)
file.append(", ");
first = false;
// Convert void* parameters to proper UNO types based on parameter type
std::u16string_view paramType = param.type;
// Determine if parameter is input-only (needs const) or output/inout (needs non-const reference)
bool isInputOnly
= (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN);
if (paramType == u"string")
{
if (isInputOnly)
{
// Input parameter: convert rtl_uString* to OUString object
file.append("OUString(static_cast<rtl_uString*>(").append(param.name).append("))");
}
else
{
// Output parameter: needs OUString reference for binding
file.append("*reinterpret_cast<OUString*>(").append(param.name).append(")");
}
}
else if (paramType == u"any")
{
if (isInputOnly)
file.append("*reinterpret_cast<const Any*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<Any*>(").append(param.name).append(")");
}
else if (paramType == u"boolean")
{
if (isInputOnly)
file.append("*reinterpret_cast<const sal_Bool*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<sal_Bool*>(").append(param.name).append(")");
}
else if (paramType == u"byte")
{
if (isInputOnly)
file.append("*reinterpret_cast<const sal_Int8*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<sal_Int8*>(").append(param.name).append(")");
}
else if (paramType == u"short")
{
if (isInputOnly)
file.append("*reinterpret_cast<const sal_Int16*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<sal_Int16*>(").append(param.name).append(")");
}
else if (paramType == u"long")
{
if (isInputOnly)
file.append("*reinterpret_cast<const sal_Int32*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<sal_Int32*>(").append(param.name).append(")");
}
else if (paramType == u"hyper")
{
if (isInputOnly)
file.append("*reinterpret_cast<const sal_Int64*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<sal_Int64*>(").append(param.name).append(")");
}
else if (paramType == u"float")
{
if (isInputOnly)
file.append("*reinterpret_cast<const float*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<float*>(").append(param.name).append(")");
}
else if (paramType == u"double")
{
if (isInputOnly)
file.append("*reinterpret_cast<const double*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<double*>(").append(param.name).append(")");
}
else if (paramType == u"type")
{
if (isInputOnly)
file.append("*reinterpret_cast<const Type*>(").append(param.name).append(")");
else
file.append("*reinterpret_cast<Type*>(").append(param.name).append(")");
}
else if (paramType.starts_with(u"[]"))
{
// Sequence type - convert with null pointer safety
OString cppTypeName = convertUnoTypeToCpp(paramType);
if (isInputOnly)
{
// Input parameter - can use temporary empty sequence
file.append("(")
.append(param.name)
.append(" ? *reinterpret_cast<const ")
.append(cppTypeName)
.append("*>(")
.append(param.name)
.append(") : ")
.append(cppTypeName)
.append("())");
}
else
{
// Output/InOut parameter - needs reference, must dereference non-null pointer
file.append("*reinterpret_cast<")
.append(cppTypeName)
.append("*>(")
.append(param.name)
.append(")");
}
}
else
{
// For interfaces and other complex types
// Check if it's an interface type that needs Reference<> wrapper
rtl::Reference<unoidl::Entity> entity;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(paramType), &entity);
if (sort == codemaker::UnoType::Sort::Interface)
{
// Interface parameters: void* from Rust points to Reference<ActualType>*
// We need to dereference and cast via UNO_QUERY to the expected interface type
OString cppTypeName = convertUnoTypeToCpp(paramType);
if (isInputOnly)
{
// For input interface parameters, we assume the Rust code passes Reference<SomeInterface>*
// We dereference it and cast via UNO_QUERY to the expected interface type
file.append("Reference<")
.append(cppTypeName)
.append(">(static_cast<Reference<XInterface>*>(")
.append(param.name)
.append(")->get(), UNO_QUERY)");
}
else
file.append("*reinterpret_cast<Reference<")
.append(cppTypeName)
.append(">*>(")
.append(param.name)
.append(")");
}
else
{
// For structs and other complex types, assume proper type cast
OString cppTypeName = convertUnoTypeToCpp(paramType);
if (isInputOnly)
{
file.append("(")
.append(param.name)
.append(" == nullptr ? ")
.append(cppTypeName)
.append("() : *reinterpret_cast<const ")
.append(cppTypeName)
.append("*>(")
.append(param.name)
.append("))");
}
else
{
file.append("*reinterpret_cast<")
.append(cppTypeName)
.append("*>(")
.append(param.name)
.append(")");
}
}
}
}
file.append(");").endLine();
// Handle return value conversion for non-void methods
if (method.returnType != u"void")
{
file.beginLine().append("// Convert result to opaque void* return").endLine();
std::u16string_view returnType = method.returnType;
if (returnType == u"string")
{
file.beginLine().append("return new OUString(result);").endLine();
}
else if (returnType == u"any")
{
file.beginLine().append("return new Any(result);").endLine();
}
else if (returnType == u"boolean")
{
file.beginLine().append("return new sal_Bool(result);").endLine();
}
else if (returnType == u"byte")
{
file.beginLine().append("return new sal_Int8(result);").endLine();
}
else if (returnType == u"short")
{
file.beginLine().append("return new sal_Int16(result);").endLine();
}
else if (returnType == u"long")
{
file.beginLine().append("return new sal_Int32(result);").endLine();
}
else if (returnType == u"hyper")
{
file.beginLine().append("return new sal_Int64(result);").endLine();
}
else if (returnType == u"float")
{
file.beginLine().append("return new float(result);").endLine();
}
else if (returnType == u"double")
{
file.beginLine().append("return new double(result);").endLine();
}
else if (returnType == u"type")
{
file.beginLine().append("return new Type(result);").endLine();
}
else if (returnType.starts_with(u"[]"))
{
// Sequence return type
file.beginLine()
.append("return new ")
.append(convertUnoTypeToCpp(returnType))
.append("(result);")
.endLine();
}
else
{
// For interfaces and other complex types, create appropriate wrapper
// Check if it's an interface type
rtl::Reference<unoidl::Entity> entity;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(returnType), &entity);
if (sort == codemaker::UnoType::Sort::Interface)
{
// Add debug output for interface return values
file.beginLine()
.append("SAL_WARN(\"rustmaker\", \"Debug: UNO method ")
.append(method.name)
.append(
" completed, result.is() = \" << (result.is() ? \"true\" : \"false\"));")
.endLine()
.beginLine()
.append("SAL_INFO(\"rust_uno_ffi\", \"Creating new Reference<")
.append(convertUnoTypeToCpp(returnType))
.append("> wrapper for method ")
.append(method.name)
.append("\");")
.endLine()
.beginLine()
.append("auto res = new Reference<")
.append(convertUnoTypeToCpp(returnType))
.append(">(result);")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!res)")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rust_uno_ffi\", \"Failed to create Reference wrapper for ")
.append(convertUnoTypeToCpp(returnType))
.append(" in method ")
.append(method.name)
.append("\");")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!res->is())")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rust_uno_ffi\", \"Reference wrapper is invalid (is() = "
"false) for ")
.append(convertUnoTypeToCpp(returnType))
.append(" in method ")
.append(method.name)
.append("\");")
.endLine()
.beginLine()
.append("delete res;")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created valid Reference "
"wrapper for ")
.append(convertUnoTypeToCpp(returnType))
.append(" in method ")
.append(method.name)
.append("\");")
.endLine()
.beginLine()
.append("return res;")
.endLine();
}
else
{
// For structs and other types
file.beginLine()
.append("return new ")
.append(convertUnoTypeToCpp(returnType))
.append("(result);")
.endLine();
}
}
}
}
void CppProducer::generateReturnValueConversion(CppFile& file,
const unoidl::InterfaceTypeEntity::Method& method)
{
file.beginLine().append("// Convert result to opaque void* return").endLine();
std::u16string_view returnType = method.returnType;
if (returnType == u"string")
{
file.beginLine().append("return new OUString(result);").endLine();
}
else if (returnType == u"any")
{
file.beginLine().append("return new Any(result);").endLine();
}
else if (returnType == u"boolean")
{
file.beginLine().append("return new sal_Bool(result);").endLine();
}
else if (returnType == u"byte")
{
file.beginLine().append("return new sal_Int8(result);").endLine();
}
else if (returnType == u"short")
{
file.beginLine().append("return new sal_Int16(result);").endLine();
}
else if (returnType == u"long")
{
file.beginLine().append("return new sal_Int32(result);").endLine();
}
else if (returnType == u"hyper")
{
file.beginLine().append("return new sal_Int64(result);").endLine();
}
else if (returnType == u"float")
{
file.beginLine().append("return new float(result);").endLine();
}
else if (returnType == u"double")
{
file.beginLine().append("return new double(result);").endLine();
}
else if (returnType == u"type")
{
file.beginLine().append("return new Type(result);").endLine();
}
else if (returnType.starts_with(u"[]"))
{
// Sequence return type - return void* to new sequence
file.beginLine()
.append("return new ")
.append(convertUnoTypeToCpp(returnType))
.append("(result);")
.endLine();
}
else
{
// For interfaces - return Reference<> to avoid instantiating abstract types
rtl::Reference<unoidl::Entity> entity;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(returnType), &entity);
if (sort == codemaker::UnoType::Sort::Interface)
{
// Interface return type - return new Reference<Interface>(result)
file.beginLine()
.append("SAL_INFO(\"rust_uno_ffi\", \"Creating Reference wrapper for return type ")
.append(convertUnoTypeToCpp(returnType))
.append("\");")
.endLine()
.beginLine()
.append("auto res = new Reference<")
.append(convertUnoTypeToCpp(returnType))
.append(">(result);")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!res)")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rust_uno_ffi\", \"Failed to create Reference wrapper for ")
.append(convertUnoTypeToCpp(returnType))
.append("\");")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!res->is())")
.endLine()
.beginBlock()
.beginLine()
.append(
"SAL_WARN(\"rust_uno_ffi\", \"Reference wrapper is invalid (is() = false) for ")
.append(convertUnoTypeToCpp(returnType))
.append("\");")
.endLine()
.beginLine()
.append("delete res;")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created valid Reference "
"wrapper for ")
.append(convertUnoTypeToCpp(returnType))
.append("\");")
.endLine()
.beginLine()
.append("return res;")
.endLine();
}
else
{
// For structs, enums and other types - return void* to new object
file.beginLine()
.append("return new ")
.append(convertUnoTypeToCpp(returnType))
.append("(result);")
.endLine();
}
}
}
// Type mapping functions (from old FFI producer)
OUString CppProducer::resolveTypedef(std::u16string_view unoType) const
{
// Recursively resolve typedefs to their underlying types
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(unoType), &entity, &cursor);
if (sort == codemaker::UnoType::Sort::Typedef)
{
rtl::Reference<unoidl::TypedefEntity> typedefEntity(
static_cast<unoidl::TypedefEntity*>(entity.get()));
if (typedefEntity.is())
{
// Recursively resolve in case of nested typedefs
OUString resolvedType = typedefEntity->getType();
return resolveTypedef(resolvedType);
}
}
// If not a typedef or resolution failed, return the original type
return OUString(unoType);
}
OString CppProducer::mapUnoPrimitiveToSal(std::u16string_view unoType)
{
// Common primitive type mappings from UNO to SAL types
if (unoType == u"boolean")
return "sal_Bool"_ostr;
else if (unoType == u"byte")
return "sal_Int8"_ostr;
else if (unoType == u"short")
return "sal_Int16"_ostr;
else if (unoType == u"unsigned short")
return "sal_uInt16"_ostr;
else if (unoType == u"long")
return "sal_Int32"_ostr;
else if (unoType == u"unsigned long")
return "sal_uInt32"_ostr;
else if (unoType == u"hyper")
return "sal_Int64"_ostr;
else if (unoType == u"unsigned hyper")
return "sal_uInt64"_ostr;
else if (unoType == u"float")
return "float"_ostr;
else if (unoType == u"double")
return "double"_ostr;
else
return OString(); // Not a primitive type
}
OString CppProducer::getCppTypeName(std::u16string_view unoType) const
{
// First, resolve any typedefs to their underlying types
OUString resolvedType = resolveTypedef(unoType);
// Map UNO types to C++ types for extern "C" functions
if (resolvedType == u"void")
return "void"_ostr;
else if (resolvedType == u"string")
return "rtl_uString*"_ostr;
else if (resolvedType == u"any" || resolvedType == u"com.sun.star.uno.Any")
return "uno_Any*"_ostr;
// Try primitive type mapping
OString primitiveType = mapUnoPrimitiveToSal(resolvedType);
if (!primitiveType.isEmpty())
return primitiveType;
// Handle complex types
if (resolvedType.startsWith(u"[]"))
return "uno_Sequence*"_ostr;
else if (isUnoStruct(resolvedType))
{
// For structs, return the FFI struct name with underscore (value type, not pointer)
OString type = u2b(resolvedType);
size_t lastDot = type.lastIndexOf('.');
if (lastDot != std::string_view::npos)
return OString::Concat(type.subView(lastDot + 1)) + "_";
else
return type + "_";
}
else if (isUnoEnum(resolvedType))
{
// For enums, return the enum name with underscore to match generated FFI enums
OString type = u2b(resolvedType);
size_t lastDot = type.lastIndexOf('.');
if (lastDot != std::string_view::npos)
return OString::Concat(type.subView(lastDot + 1)) + "_";
else
return type + "_";
}
else if (isUnoConstantGroup(resolvedType))
{
// For constant groups, return the FFI name with underscore
OString type = u2b(resolvedType);
size_t lastDot = type.lastIndexOf('.');
if (lastDot != std::string_view::npos)
return OString::Concat(type.subView(lastDot + 1)) + "_";
else
return type + "_";
}
else if (isUnoType(resolvedType))
{
// For interfaces and other types, return XInterface* pointer
return "XInterface*"_ostr;
}
else
return "void*"_ostr; // Default to void* for unknown types
}
OString CppProducer::getRustFFITypeName(std::u16string_view unoType) const
{
// Map UNO types to Rust FFI types
if (unoType == u"string")
return "*mut rtl_uString"_ostr;
else if (unoType == u"boolean")
return "u8"_ostr; // sal_Bool is typedef'd to u8 (unsigned char)
else if (unoType == u"byte")
return "i8"_ostr;
else if (unoType == u"short")
return "i16"_ostr;
else if (unoType == u"unsigned short")
return "u16"_ostr;
else if (unoType == u"long")
return "i32"_ostr;
else if (unoType == u"unsigned long")
return "u32"_ostr;
else if (unoType == u"hyper")
return "i64"_ostr;
else if (unoType == u"unsigned hyper")
return "u64"_ostr;
else if (unoType == u"float")
return "f32"_ostr;
else if (unoType == u"double")
return "f64"_ostr;
else if (unoType == u"void")
return "()"_ostr;
else if (unoType == u"any" || unoType == u"com.sun.star.uno.Any")
return "*mut uno_Any"_ostr; // UNO Any type
else if (isUnoStruct(unoType))
{
// For structs, return just the struct name with underscore since we import it
OString type = u2b(unoType);
size_t lastDot = type.lastIndexOf('.');
if (lastDot != std::string_view::npos)
return OString::Concat(type.subView(lastDot + 1)) + "_";
else
return type + "_";
}
else if (isUnoEnum(unoType))
{
// For enums, return just the enum name since we import it
OString type = u2b(unoType);
size_t lastDot = type.lastIndexOf('.');
if (lastDot != std::string_view::npos)
return OString(type.subView(lastDot + 1));
else
return type;
}
else if (isUnoConstantGroup(unoType))
{
// For constant groups, return the constant group name with underscore to match C++ side
OString type = u2b(unoType);
size_t lastDot = type.lastIndexOf('.');
if (lastDot != std::string_view::npos)
return OString::Concat(type.subView(lastDot + 1)) + "_";
else
return type + "_";
}
else if (isUnoType(unoType))
{
// For interfaces, return *mut XInterface pointer
return "*mut XInterface"_ostr;
}
else
return "*mut std::ffi::c_void"_ostr; // Default for unknown types
}
OString CppProducer::convertBasicType(const OString& typeName)
{
OString result = typeName;
// Convert UNO primitive types to C++ types
if (result == "long")
result = "sal_Int32"_ostr;
else if (result == "short")
result = "sal_Int16"_ostr;
else if (result == "byte")
result = "sal_Int8"_ostr;
else if (result == "boolean")
result = "sal_Bool"_ostr;
else if (result == "double")
result = "double"_ostr;
else if (result == "float")
result = "float"_ostr;
else if (result == "string")
result = "OUString"_ostr;
else if (result == "any")
result = "Any"_ostr;
else if (result.indexOf('.') != -1)
{
// For UNO types with namespace, convert dots to double colons
result = result.replaceAll("."_ostr, "::"_ostr);
}
return result;
}
// Type classification helper methods
bool CppProducer::isUnoType(std::u16string_view typeName) const
{
// Check if this is a valid UNO type using TypeManager
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor);
// Return true for any valid UNO type (not just interfaces/structs/enums)
return sort != codemaker::UnoType::Sort::Void && sort != codemaker::UnoType::Sort::Boolean
&& sort != codemaker::UnoType::Sort::Byte && sort != codemaker::UnoType::Sort::Short
&& sort != codemaker::UnoType::Sort::UnsignedShort
&& sort != codemaker::UnoType::Sort::Long
&& sort != codemaker::UnoType::Sort::UnsignedLong
&& sort != codemaker::UnoType::Sort::Hyper
&& sort != codemaker::UnoType::Sort::UnsignedHyper
&& sort != codemaker::UnoType::Sort::Float && sort != codemaker::UnoType::Sort::Double
&& sort != codemaker::UnoType::Sort::Char && sort != codemaker::UnoType::Sort::String
&& sort != codemaker::UnoType::Sort::Type && sort != codemaker::UnoType::Sort::Any;
}
bool CppProducer::isUnoStruct(std::u16string_view typeName) const
{
// Check the type sort using TypeManager
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor);
// Return true if it's a plain struct or polymorphic struct template
return (sort == codemaker::UnoType::Sort::PlainStruct);
}
bool CppProducer::isUnoEnum(std::u16string_view typeName) const
{
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor);
return sort == codemaker::UnoType::Sort::Enum;
}
bool CppProducer::isUnoPolymorphicStruct(std::u16string_view typeName) const
{
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor);
return sort == codemaker::UnoType::Sort::PolymorphicStructTemplate;
}
bool CppProducer::isUnoConstantGroup(std::u16string_view typeName) const
{
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor);
return sort == codemaker::UnoType::Sort::ConstantGroup;
}
bool CppProducer::isUnoInterface(std::u16string_view typeName) const
{
rtl::Reference<unoidl::Entity> entity;
rtl::Reference<unoidl::MapCursor> cursor;
codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor);
return sort == codemaker::UnoType::Sort::Interface;
}
OString CppProducer::convertUnoTypeToHandle(std::u16string_view unoType)
{
// Convert UNO type name to typedef handle name
// com.sun.star.lang.XMain -> com__sun__star__lang__XMainHandle
OString functionPrefix = getCFunctionPrefix(std::string(unoType.begin(), unoType.end()));
return functionPrefix + "Handle";
}
void CppProducer::generateServiceHeader(
std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity)
{
if (m_dryRun || !m_combinedHeaderFile)
return;
CppFile& file = *m_combinedHeaderFile;
file.openFileAppend();
// Add pragma once for header guard
file.beginLine().append("#pragma once").endLine().endLine();
// Include the service's interface
OUString interfaceType = entity->getBase();
std::string includePath(u2b(interfaceType));
std::replace(includePath.begin(), includePath.end(), '.', '/');
file.beginLine().append("#include <").append(includePath).append(".hpp>").endLine();
file.beginLine().append("#include <com/sun/star/uno/XComponentContext.hpp>").endLine();
file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine();
file.endLine();
// Generate extern "C" declarations for service creation
OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming
file.beginLine().append("extern \"C\"").endLine().beginBlock();
file.beginLine().append(" // Service creation functions for ").append(name).endLine();
file.beginLine()
.append(" SAL_DLLPUBLIC_EXPORT void* ")
.append(functionPrefix)
.append("_create(void* context_handle);")
.endLine();
file.beginLine().append("}").endLine();
file.closeFile();
}
void CppProducer::generateServiceSource(
std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity)
{
if (m_dryRun || !m_combinedSourceFile)
return;
CppFile& file = *m_combinedSourceFile;
file.openFileAppend();
// Include the header
std::string headerPath(name);
std::replace(headerPath.begin(), headerPath.end(), '.', '/');
file.beginLine().append("#include <com/sun/star/lang/XMultiComponentFactory.hpp>").endLine();
file.beginLine().append("#include <sal/log.hxx>").endLine();
file.endLine();
// Services don't need namespace declarations - use fully qualified names instead
// Generate service creation function
OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming
OUString interfaceType = entity->getBase();
std::string interfaceCppType(u2b(interfaceType));
std::replace(interfaceCppType.begin(), interfaceCppType.end(), '.', ':');
// Fix single colons to double colons for C++ namespace syntax
size_t colonPos = 0;
while ((colonPos = interfaceCppType.find(":", colonPos)) != std::string::npos)
{
if (colonPos == 0 || interfaceCppType[colonPos - 1] != ':')
{
interfaceCppType.insert(colonPos, ":");
colonPos += 2;
}
else
{
colonPos++;
}
}
if (!interfaceCppType.empty() && interfaceCppType.substr(0, 2) != "::")
{
interfaceCppType = "::" + interfaceCppType;
}
file.beginLine().append("extern \"C\"").endLine().beginBlock();
file.beginLine()
.append("SAL_DLLPUBLIC_EXPORT void* ")
.append(functionPrefix)
.append("_create(void* context_handle)")
.endLine()
.beginBlock()
.beginLine()
.append("try")
.endLine()
.beginBlock()
.beginLine()
.append("Reference<com::sun::star::uno::XComponentContext>* ctx = "
"static_cast<Reference<com::sun::star::uno::XComponentContext>*>(context_handle);")
.endLine()
.beginLine()
.append("if (!ctx || !ctx->is()) return nullptr;")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("Reference<com::sun::star::lang::XMultiComponentFactory> factory = "
"(*ctx)->getServiceManager();")
.endLine()
.beginLine()
.append("if (!factory.is()) return nullptr;")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("Reference<")
.append("com::sun::star::uno::XInterface")
.append("> service = Reference<")
.append("com::sun::star::uno::XInterface")
.append(">(")
.endLine()
.extraIndent()
.beginLine()
.append("factory->createInstanceWithContext(\"")
.append(name)
.append("\", *ctx), UNO_QUERY);")
.endLine()
.beginLine()
.append("")
.endLine()
.beginLine()
.append("if (!service.is()) return nullptr;")
.endLine()
.beginLine()
.append("return new Reference<")
.append("com::sun::star::uno::XInterface")
.append(">(service);")
.endLine()
.endBlock()
.beginLine()
.append("catch (const Exception& ex)")
.endLine()
.beginBlock()
.beginLine()
.append("SAL_WARN(\"rustmaker\", \"Service creation failed for ")
.append(name)
.append(": \" << ex.Message);")
.endLine()
.beginLine()
.append("return nullptr;")
.endLine()
.endBlock()
.endBlock();
file.beginLine().append("}").endLine();
file.closeFile();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */