#pragma once #include "ArgsFromClient.hpp" #include "ArgsToClient.hpp" #include namespace fluid { namespace client { template struct FluidSCMessaging{ static auto getInterfaceTable(){ return FluidSCWrapper::getInterfaceTable(); } static auto getName(){ return FluidSCWrapper::getName(); } template struct MessageDispatchCmd { using Descriptor = typename Client::MessageSetType::template MessageDescriptorAt; using ArgTuple = typename Descriptor::ArgumentTypes; using ReturnType = typename Descriptor::ReturnType; using IndexList = typename Descriptor::IndexList; static constexpr size_t Message = N; index id; ArgTuple args; ReturnType result; std::string name; IndexList argIndices; void* replyAddr{nullptr}; }; template struct SetupMessageCmd { void operator()(const T& message) { static std::string messageName = std::string{getName()} + '/' + message.name; auto ft = getInterfaceTable(); ft->fDefinePlugInCmd(messageName.c_str(), doMessage,(void*)messageName.c_str()); } }; template static bool validateMessageArgs(Message* msg, sc_msg_iter* inArgs) { using ArgTuple = decltype(msg->args); std::string tags(inArgs->tags + inArgs->count);//evidently this needs commenting: construct string at pointer offset by tag count, to pick up args bool willContinue = true; bool typesMatch = true; auto& args = msg->args; constexpr size_t expectedArgCount = std::tuple_size::value; /// TODO this squawks if we have a completion message, so maybe we can check if extra arg is a 'b' and squawk if not? // if(tags.size() > expectedArgCount) // { // std::cout << "WARNING: " << msg->name << " received more arguments than expected (got " // << tags.size() << ", expect " << expectedArgCount << ")\n"; // } if(tags.size() < expectedArgCount) { std::cout << "ERROR: " << msg->name << " received fewer arguments than expected (got " << tags.size() << ", expect " << expectedArgCount << ")\n"; willContinue = false; } auto tagsIter = tags.begin(); auto tagsEnd = tags.end(); ForEach(args,[&typesMatch,&tagsIter,&tagsEnd](auto& arg){ if(tagsIter == tagsEnd) { typesMatch = false; return; } char t = *(tagsIter++); typesMatch = typesMatch && ParamReader::argTypeOK(arg,t); }); willContinue = willContinue && typesMatch; if(!typesMatch) { auto& report = std::cout; report << "ERROR: " << msg->name << " type signature incorrect.\nExpect: ("; size_t i{0}; ForEach(args, [&i](auto& x){ std::cout << ParamReader::argTypeToString(x); if(i < (std::tuple_size::value - 1 ) ) { std::cout << " ,"; } i++; }); report << ")\nReceived: ("; i = 0; for(auto t: tags) { report << ParamReader::oscTagToString(t); if( i < ( tags.size() - 1 ) ) { report << ", "; } i++; } report << ")\n"; } return willContinue; } template static void doMessage(World* inWorld, void* inUserData, struct sc_msg_iter* args, void* replyAddr) { using MessageData = MessageDispatchCmd; auto msg = new MessageData(); msg->id = args->geti(); msg->replyAddr = copyReplyAddress(replyAddr); ///TODO make this step contingent on verbosity or something, in the name of effieciency bool willContinue = validateMessageArgs(msg, args); if(!willContinue) { delete msg; return; } msg->name = std::string{'/'} + (const char*)(inUserData); ForEach(msg-> args,[inWorld,&args](auto& thisarg) { thisarg = ParamReader::fromArgs(inWorld, *args,thisarg,0); }); size_t completionMsgSize{args ? args->getbsize() : 0}; assert(completionMsgSize <= std::numeric_limits::max()); char* completionMsgData = nullptr; if (completionMsgSize) { completionMsgData = (char*)getInterfaceTable()->fRTAlloc(inWorld, completionMsgSize); args->getb(completionMsgData, completionMsgSize); } getInterfaceTable()->fDoAsynchronousCommand(inWorld, replyAddr, getName(), msg, [](World* world, void* data) // NRT thread: invocation { MessageData* m = static_cast(data); using ReturnType = typename MessageData::ReturnType; if(auto ptr = FluidSCWrapper::get(m->id).lock()) { m->result = ReturnType{invokeImpl(ptr->mClient, m->args,m->argIndices)}; if (!m->result.ok()) FluidSCWrapper::printResult(world, m->result); } else FluidSCWrapper::printNotFound(m->id); return true; }, [](World* world, void* data) // RT thread: buffer swap (and possible completion messages) { MessageData* m = static_cast(data); MessageData::Descriptor::template forEachArg(m->args, world); return true; }, [](World*, void* data) // NRT Thread: Send reply { MessageData* m = static_cast(data); if(m->result.status() != Result::Status::kError) messageOutput(m->name, m->id, m->result, m->replyAddr); return false; }, [](World*, void* data) // RT thread: clean up { MessageData* m = static_cast(data); delete m; }, static_cast(completionMsgSize), completionMsgData); } template // Call from NRT static decltype(auto) invokeImpl(Client& x, ArgsTuple& args, std::index_sequence) { return x.template invoke(x, std::get(args)...); } template // call from RT static void messageOutput(const std::string& s, index id, MessageResult& result, void* replyAddr) { index numTags = ToOSCTypes::numTags(static_cast(result)); if(numTags > 2048) { std::cout << "ERROR: Message response too big to send (" << asUnsigned(numTags) * sizeof(float) << " bytes)." << std::endl; return; } small_scpacket packet; packet.adds(s.c_str()); packet.maketags(static_cast(numTags) + 2); packet.addtag(','); packet.addtag('i'); ToOSCTypes::getTag(packet, static_cast(result)); packet.addi(static_cast(id)); ToOSCTypes::convert(packet, static_cast(result)); if(replyAddr) ::SendReply(static_cast(replyAddr),packet.data(),static_cast(packet.size())); } static void messageOutput(const std::string& s,index id, MessageResult&, void* replyAddr) { small_scpacket packet; packet.adds(s.c_str()); packet.maketags(2); packet.addtag(','); packet.addtag('i'); packet.addi(static_cast(id)); if(replyAddr) ::SendReply(static_cast(replyAddr),packet.data(),static_cast(packet.size())); } template static void messageOutput(const std::string& s, index id, MessageResult>& result, void* replyAddr) { using T = std::tuple; index numTags = ToOSCTypes::numTags(static_cast(result)); if(numTags > 2048) { std::cout << "ERROR: Message response too big to send (" << asUnsigned(numTags) * sizeof(float) << " bytes)." << std::endl; return; } small_scpacket packet; packet.adds(s.c_str()); packet.maketags(static_cast(numTags + 3)); packet.addtag(','); packet.addtag('i'); ToOSCTypes::getTag(packet,static_cast(result)); packet.addi(static_cast(id)); ToOSCTypes::convert(packet, static_cast(result)); if(replyAddr) ::SendReply(static_cast(replyAddr),packet.data(),static_cast(packet.size())); } }; } }