Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[4diac-dev] InterfaceSpecBuilder

Hi!

For iterating access to PostgreSQL result with multiple rows I went with a generic SIFBs (CGenFunctionBlock<CFunctionBlock>). I need some additional events and outputs and it doesn't make sense to do that through a comm layer.

So I started writing my own GEN_PQ_ITER and everything was fine until I needed to fill out SFBInterfaceSpec. It's a nightmare to set everything up before filling it, and it's also not flexible at all. Further port changes are the same problems all over again + refactoring of the previous code.

To simplify things I made SFBInterfaceSpec (source code attached). Does it make sense to incorporate it into Forte core? I also think that a parser for generic SIFBs should be standardized as currently every GEN_* implements its own parser.


An example would look like this:

bool GEN_PQ_ITER::createInterfaceSpec(const char *pa_sConfig, SFBInterfaceSpec &pa_oInterfaceSpec) {
    unsigned long numbers[2];
    if (!parseSpec(pa_sConfig, "PQ_ITER", numbers, 2))
        return false;

    static const std::array staticEINames = { g_nStringIdINIT, g_nStringIdREQ };
    static const std::array staticEONames = { g_nStringIdINITO, g_nStringIdCNF };

    auto ifsb = CIfSpecBuilder();
    bool stat =
        ifsb.m_oEI.setStaticEvents(staticEINames) &&
        ifsb.m_oEO.setStaticEvents(staticEONames) &&
        ifsb.m_oDI.addData("QI", g_nStringIdBYTE) && /* g_nStringIdQI can be used instead of "QI" */
        ifsb.m_oDI.addDataRange("SD_", (int)numbers[0]) &&
        ifsb.m_oDO.addData("QO", g_nStringIdBYTE) &&
        ifsb.m_oDO.addDataRange("RD_", (int)numbers[1]) &&
        ifsb.bind(ifsb.m_oEI["INIT"], {ifsb.m_oDI["QI"], ifsb.m_oDI["ID"]}) &&
        ifsb.bind(ifsb.m_oEI["REQ"], ifsb.m_oDI["QI"]) &&
        ifsb.bind(ifsb.m_oEO["INITO"], ifsb.m_oDO["QO"]);

    stat = stat && ifsb.build(m_oMixedStorage, pa_oInterfaceSpec);
    if (!stat) {
        DEVLOG_ERROR("interface spec builder failed!\n");
    }

    return stat;
}

As it can be seen, the builder supports both static and dynamic configuration (although not yet for "withs"). And in the end it uses only one member for data storage (m_oMixedStorage) which automatically cleans up at SIFB's EOL.

I don't expect this method to use more memory than before. It even has potential to use less memory as there is only one memory block used (after setup). The only disadvantage would be slower initialization which shouldn't matter any way. By using this way of initialization in other GEN_* FBs the code size could also be reduced a bit.

It would be nice to be able to use constexpr for std::array (so it can be stored in ROM), but something needs to be done first, I need to look into it.

#include <algorithm>

#include "ifSpecBuilder.h"
#ifdef FORTE_ENABLE_GENERATED_SOURCE_CPP
#include "ifSpecBuilder_gen.cpp"
#endif
#include "mixedStorage.h"


bool CStringIdListSpecBuilder::setStaticList(const CStringDictionary::TStringId *pa_aunStaticList, std::size_t pa_nItemsCount) {
	if (pa_nItemsCount > m_unMaxItems)
		return false;
	m_vDynamicList.clear();
	m_aunStaticList = pa_aunStaticList;
	m_unStaticListSize = (TForteUInt8)pa_nItemsCount;
	return true;
}

bool CStringIdListSpecBuilder::addString(CStringDictionary::TStringId pa_unString) {
	if (m_aunStaticList) {
		m_vDynamicList.insert(m_vDynamicList.end(), m_aunStaticList, m_aunStaticList + m_unStaticListSize);
		m_aunStaticList = nullptr;
	}
	m_unStaticListSize = 0;
	if (m_vDynamicList.size() >= m_unMaxItems)
		return false;
	m_vDynamicList.push_back(pa_unString);
	return true;
}

bool CStringIdListSpecBuilder::addString(const char *pa_sString) {
	return addString(CStringDictionary::getInstance().insert(pa_sString));
}

int CStringIdListSpecBuilder::findString(const char *pa_sString) const {
	auto nString = CStringDictionary::getInstance().getId(pa_sString);
	if (nString == CStringDictionary::scm_nInvalidStringId)
		return -1;
	return findString(nString);
}

int CStringIdListSpecBuilder::findString(CStringDictionary::TStringId pa_unString) const {
	int retVal = -1;
	if (m_aunStaticList) {
		const auto end = m_aunStaticList + m_unStaticListSize;
		auto pos = std::find(m_aunStaticList, end, pa_unString);
		if (pos != end) {
			retVal = (int)(pos - m_aunStaticList);
		}
	} else {
		auto pos = std::find(m_vDynamicList.begin(), m_vDynamicList.end(), pa_unString);
		if (pos != m_vDynamicList.end()) {
			retVal = (int)(pos - m_vDynamicList.begin());
		}
	}
	return retVal;
}

std::size_t CStringIdListSpecBuilder::calcStorageSize() const {
	return m_aunStaticList ? 0 : m_vDynamicList.size() * sizeof(CStringDictionary::TStringId);
}

std::tuple<const CStringDictionary::TStringId*, TForteUInt8> CStringIdListSpecBuilder::build(CMixedStorage &pa_oStorage) const {
	if (m_aunStaticList) {
		return {m_aunStaticList, m_unStaticListSize};
	}
	const CStringDictionary::TStringId* listPtr = pa_oStorage.write(m_vDynamicList.data(), m_vDynamicList.size());
	return {listPtr, (TForteUInt8)m_vDynamicList.size()};
}


bool CDataSpecBuilderBase::addDataRange(const char *pa_sPrefix, int pa_nRangeSize) {
	TIdentifier name;
	bool retVal = true;
	for (int i = 0; i < pa_nRangeSize; ++i) {
		if (snprintf(name, sizeof(name), "%s%u", pa_sPrefix, i + 1) >= (int)sizeof(name))
			return false;
		retVal = retVal && addData(name, g_nStringIdANY);
	}
	return retVal;
}

std::tuple<const CStringDictionary::TStringId*, const CStringDictionary::TStringId*, TForteUInt8>
CDataSpecBuilderBase::build(CMixedStorage &pa_oStorage) const {
	auto nameData = m_oNamesListBuilder.build(pa_oStorage);
	auto typeData = m_oTypesListBuilder.build(pa_oStorage);
	return {std::get<0>(nameData), std::get<0>(typeData), std::get<1>(nameData) };
}


void CWithSpecBuilderBase::grow(std::size_t pa_unNumEvents) {
	if (pa_unNumEvents > m_vvDynamicList.size()) {
		m_vvDynamicList.resize(pa_unNumEvents);
	}
}

bool CWithSpecBuilderBase::bind(TForteUInt8 pa_unEventId, TForteUInt8 pa_unDataId) {
	grow(pa_unEventId + 1);
	m_vvDynamicList[pa_unEventId].push_back(pa_unDataId);
	return true;
}

std::size_t CWithSpecBuilderBase::calcStorageSize() const {
	std::size_t sumSize = m_vvDynamicList.size();
	for (const auto &lst : m_vvDynamicList) {
		if (!lst.empty()) {
			sumSize += (lst.size() + 1) * sizeof(TDataIOID);
		}
		sumSize += sizeof(TForteInt16);
	}
	return sumSize;
}

std::tuple<const TDataIOID*, const TForteInt16*> CWithSpecBuilderBase::build(CMixedStorage &pa_oStorage) const {
	auto pWiths = pa_oStorage.end<TDataIOID>();
	for (const auto &lst : m_vvDynamicList) {
		if (!lst.empty()) {
			pa_oStorage.write(lst.data(), lst.size());
			pa_oStorage.write<TDataIOID>(255);
		}
	}
	auto pIndexes = pa_oStorage.end<TForteInt16>();
	TForteInt16 idx = 0;
	for (const auto &lst : m_vvDynamicList) {
		if (lst.empty()) {
			pa_oStorage.write<TForteInt16>(-1);
		} else {
			pa_oStorage.write(idx);
			idx += (TForteInt16)(lst.size() + 1);
		}
	}
	return {pWiths, pIndexes};
}


bool CIfSpecBuilder::build(CMixedStorage &pa_oStorage, SFBInterfaceSpec &pa_oInterfaceSpec) const {
	std::size_t storageSize =
		m_oEI.calcStorageSize() + m_oEO.calcStorageSize() +
		m_oDI.calcStorageSize() + m_oDI.calcStorageSize() +
		m_oIWith.calcStorageSize() + m_oOWith.calcStorageSize();
	pa_oStorage.reserve(storageSize);
	std::tie(pa_oInterfaceSpec.m_aunEINames, pa_oInterfaceSpec.m_nNumEIs) = m_oEI.build(pa_oStorage);
	std::tie(pa_oInterfaceSpec.m_aunEONames, pa_oInterfaceSpec.m_nNumEOs) = m_oEO.build(pa_oStorage);
	std::tie(
		pa_oInterfaceSpec.m_aunDINames, pa_oInterfaceSpec.m_aunDIDataTypeNames,
		pa_oInterfaceSpec.m_nNumDIs
	) = m_oDI.build(pa_oStorage);
	std::tie(
		pa_oInterfaceSpec.m_aunDONames, pa_oInterfaceSpec.m_aunDODataTypeNames,
		pa_oInterfaceSpec.m_nNumDOs
	) = m_oDO.build(pa_oStorage);
	std::tie(pa_oInterfaceSpec.m_anEIWith, pa_oInterfaceSpec.m_anEIWithIndexes) = m_oIWith.build(pa_oStorage);
	std::tie(pa_oInterfaceSpec.m_anEOWith, pa_oInterfaceSpec.m_anEOWithIndexes) = m_oOWith.build(pa_oStorage);

	return pa_oStorage.size() <= storageSize;
}
/*********************************************************************
* Copyright (c) 2022
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
*   Davor Cihlar
*     - initial implementation
**********************************************************************/

#ifndef _IF_SPEC_BUILDER_H_
#define _IF_SPEC_BUILDER_H_

#include <vector>
#include <array>
#include <tuple>
#include <funcbloc.h>


class CMixedStorage;

class CInputSpecTag;
class COutputSpecTag;
class CDataSpecTag;
class CEventSpecTag;

template<class TypeTag, class DirTag>
class CSpecReference {
	public:
		constexpr CSpecReference() = default;
		constexpr CSpecReference(int pa_nRef) {
			if (pa_nRef < 0 || pa_nRef >= maxVal) {
				ref = maxVal;
			} else {
				ref = (TDataIOID)pa_nRef;
			}
		}

		constexpr bool isValid() const {
			return ref != maxVal;
		}

		constexpr auto operator*() const {
			return ref;
		}

	private:
		static constexpr auto maxVal = std::numeric_limits<TDataIOID>::max();

		TDataIOID ref = maxVal;
};

class CStringIdListSpecBuilder {
	public:
		CStringIdListSpecBuilder(std::size_t pa_unMaxItems) : m_unMaxItems(pa_unMaxItems)
		{}

		bool setStaticList(const CStringDictionary::TStringId *pa_aunStaticList, std::size_t pa_nItemsCount);
		bool addString(const CStringDictionary::TStringId pa_unString);
		bool addString(const char *pa_sString);

		int findString(const char *pa_sString) const;
		int findString(CStringDictionary::TStringId pa_unString) const;

		std::size_t calcStorageSize() const;
		std::tuple<const CStringDictionary::TStringId*, TForteUInt8> build(CMixedStorage &pa_oStorage) const;

	private:
		const std::size_t m_unMaxItems;
		std::vector<CStringDictionary::TStringId> m_vDynamicList;
		TForteUInt8 m_unStaticListSize = 0;
		const CStringDictionary::TStringId *m_aunStaticList = nullptr;
};

class CEventSpecBuilderBase {
	public:
		bool setStaticEvents(const CStringDictionary::TStringId *pa_aunStaticNames, std::size_t pa_nEventsCount) {
			return m_oNamesListBuilder.setStaticList(pa_aunStaticNames, pa_nEventsCount);
		}
		template<std::size_t N>
		bool setStaticEvents(const std::array<CStringDictionary::TStringId, N> &pa_aStaticNames) {
			return setStaticEvents(pa_aStaticNames.data(), pa_aStaticNames.size());
		}
		bool addEvent(CStringDictionary::TStringId pa_unName) {
			return m_oNamesListBuilder.addString(pa_unName);
		}
		bool addEvent(const char *pa_sName) {
			return m_oNamesListBuilder.addString(pa_sName);
		}

		template<typename T>
		int findEvent(T pa_tName) const {
			return m_oNamesListBuilder.findString(pa_tName);
		}

		auto calcStorageSize() const {
			return m_oNamesListBuilder.calcStorageSize();
		}
		auto build(CMixedStorage &pa_oStorage) const {
			return m_oNamesListBuilder.build(pa_oStorage);
		}

	private:
		static constexpr auto scm_unMaxEvents = CFunctionBlock::scm_nMaxInterfaceEvents;
		CStringIdListSpecBuilder m_oNamesListBuilder{scm_unMaxEvents};
};

template<class DirTag>
class CEventSpecBuilder : public CEventSpecBuilderBase {
	public:
		template<typename TNameType>
		CSpecReference<CEventSpecTag, DirTag> operator[] (TNameType pa_tName) const {
			return { findEvent(pa_tName) };
		}
};

class CDataSpecBuilderBase {
	public:
		bool setStaticData(const CStringDictionary::TStringId *pa_aunStaticDataNames, const CStringDictionary::TStringId *pa_aunStaticTypeNames, std::size_t pa_nDataCount) {
			return
				m_oNamesListBuilder.setStaticList(pa_aunStaticDataNames, pa_nDataCount) &&
				m_oTypesListBuilder.setStaticList(pa_aunStaticTypeNames, pa_nDataCount);
		}
		bool addData(CStringDictionary::TStringId pa_unName, CStringDictionary::TStringId pa_unTypeName) {
			return m_oNamesListBuilder.addString(pa_unName) && m_oTypesListBuilder.addString(pa_unTypeName);
		}
		bool addData(const char *pa_sName, CStringDictionary::TStringId pa_unTypeName) {
			return m_oNamesListBuilder.addString(pa_sName) && m_oTypesListBuilder.addString(pa_unTypeName);
		}
		bool addDataRange(const char *pa_sPrefix, int pa_nRangeSize);

		template<typename T>
		int findData(T pa_tName) const {
			return m_oNamesListBuilder.findString(pa_tName);
		}

		std::size_t calcStorageSize() const {
			return m_oNamesListBuilder.calcStorageSize() + m_oTypesListBuilder.calcStorageSize();
		}
		std::tuple<const CStringDictionary::TStringId*, const CStringDictionary::TStringId*, TForteUInt8> build(CMixedStorage &pa_oStorage) const;

	private:
		static constexpr auto scm_unMaxData = CFunctionBlock::scm_nMaxInterfaceEvents;
		CStringIdListSpecBuilder m_oNamesListBuilder{scm_unMaxData};
		CStringIdListSpecBuilder m_oTypesListBuilder{scm_unMaxData};
};

template<class DirTag>
class CDataSpecBuilder : public CDataSpecBuilderBase {
	public:
		template<typename TNameType>
		CSpecReference<CDataSpecTag, DirTag> operator[] (TNameType pa_tName) const {
			return { findData(pa_tName) };
		}
};

class CWithSpecBuilderBase {
	public:
		//TODO: setStaticBindings()
		bool bind(TDataIOID pa_unEventId, TDataIOID pa_unDataId);
		bool bind(TDataIOID pa_unEventId, std::initializer_list<TDataIOID> pa_aunDataIds) {
			bool retVal = true;
			for (auto dataId : pa_aunDataIds) {
				retVal = retVal && bind(pa_unEventId, dataId);
			}
			return retVal;
		}
		
		std::size_t calcStorageSize() const;
		std::tuple<const TDataIOID*, const TForteInt16*> build(CMixedStorage &pa_oStorage) const;

	private:
		std::vector<std::vector<TDataIOID>> m_vvDynamicList;

		void grow(std::size_t pa_unNumEvents);
};

template<class DirTag>
class CWithSpecBuilder : public CWithSpecBuilderBase {
	public:
		bool bind(CSpecReference<CEventSpecTag, DirTag> pa_oEventRef, CSpecReference<CDataSpecTag, DirTag> pa_oDataRef) {
			return pa_oEventRef.isValid() && pa_oDataRef.isValid() && CWithSpecBuilderBase::bind(*pa_oEventRef, *pa_oDataRef);
		}
		bool bind(CSpecReference<CEventSpecTag, DirTag> pa_oEventRef, std::initializer_list<CSpecReference<CDataSpecTag, DirTag>> pa_aoDataRefs) {
			bool retVal = pa_oEventRef.isValid();
			for (auto ref : pa_aoDataRefs) {
				retVal = retVal && ref.isValid() && bind(pa_oEventRef, *ref);
			}
			return retVal;
		}
};

class CIfSpecBuilder {
	public:
		CEventSpecBuilder<CInputSpecTag>  m_oEI;
		CEventSpecBuilder<COutputSpecTag> m_oEO;
		CDataSpecBuilder<CInputSpecTag>   m_oDI;
		CDataSpecBuilder<COutputSpecTag>  m_oDO;
		CWithSpecBuilder<CInputSpecTag>   m_oIWith;
		CWithSpecBuilder<COutputSpecTag>  m_oOWith;

		template<class DirTag>
		bool bind(CSpecReference<CEventSpecTag, DirTag> pa_oEventRef, CSpecReference<CDataSpecTag, DirTag> pa_oDataRef) {
			return getWithFromDir(pa_oEventRef).bind(pa_oEventRef, pa_oDataRef);
		}
		template<class DirTag>
		bool bind(CSpecReference<CEventSpecTag, DirTag> pa_oEventRef, std::initializer_list<CSpecReference<CDataSpecTag, DirTag>> &&pa_loDataRef) {
			return getWithFromDir(pa_oEventRef).bind(pa_oEventRef, std::move(pa_loDataRef));
		}

		bool build(CMixedStorage &pa_oStorage, SFBInterfaceSpec &pa_oInterfaceSpec) const;

	private:
		constexpr CWithSpecBuilder<CInputSpecTag>& getWithFromDir(CSpecReference<CEventSpecTag, CInputSpecTag>) {
			return m_oIWith;
		}
		constexpr CWithSpecBuilder<COutputSpecTag>& getWithFromDir(CSpecReference<CEventSpecTag, COutputSpecTag>) {
			return m_oOWith;
		}
};

#endif /* _IF_SPEC_BUILDER_H_ */
/*********************************************************************
* Copyright (c) 2022
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
*   Davor Cihlar
*     - initial implementation
**********************************************************************/

#ifndef _MIXED_STORAGE_H_
#define _MIXED_STORAGE_H_

#include <vector>


class CMixedStorage {
	public:
		void clear() { m_vData.clear(); }
		auto size() { return m_vData.size(); }
		void reserve(std::size_t pa_unSize) { m_vData.reserve(pa_unSize); }

		template<typename T>
		T* write(const T *pa_pBegin, const T *pa_pEnd) {
			std::size_t bytesSize = (pa_pEnd - pa_pBegin) * sizeof(T);
			return reinterpret_cast<T*>(write(pa_pBegin, bytesSize));
		}
		template<typename T>
		T* write(const T *pa_pBegin, std::size_t pa_unSize) {
			return reinterpret_cast<T*>(write(reinterpret_cast<const void*>(pa_pBegin), pa_unSize * sizeof(T)));
		}
		template<typename T>
		T* write(T pa_nValue) {
			return reinterpret_cast<T*>(write(reinterpret_cast<void*>(&pa_nValue), sizeof(pa_nValue)));
		}

		void* write(const void *pa_pBegin, std::size_t pa_unSize);
		
		template<typename T>
		T* end() {
			return reinterpret_cast<T*>(&*m_vData.end());
		}
		template<typename T>
		const T* end() const {
			return reinterpret_cast<const T*>(&*m_vData.end());
		}

	private:
		std::vector<char> m_vData;
};

#endif /*_MIXED_STORAGE_H_ */

Back to the top