Resolve "Refactor API conversion types"

This MR separates the functionality of managed_rpc_type<T> into dedicated lifetime management types and conversion functions. This makes for clearer the code since it is now possible to know when a conversion is performed.

Lifetime management for C-types is now done with the managed_ctype<T> types, which act only as holders of dynamically allocated C types and do not perform any type conversions whatsoever.

Type conversions are now done with the T1 convert(T2 obj) family of functions. When converting from CXX to C API types, these functions effectively return instances of the appropriate managed_ctype<T>s. If one wishes to relinquish ownership of the content of the managed_ctype<T> (e.g. to pass it to a pure C function that will release it), this can be done with the managed_ctype<T>::release() function.

Closes #46 (closed)

Edited by Alberto Miranda

Merge request reports

Loading
+40 −0
Changes for src/common/api/CMakeLists.txt: 40 added lines, 0 removed lines.
Original line number Diff line number Diff line
################################################################################
# Copyright 2021-2022, Barcelona Supercomputing Center (BSC), Spain            #
#                                                                              #
# This software was partially supported by the EuroHPC-funded project ADMIRE   #
#   (Project ID: 956748, https://www.admire-eurohpc.eu).                       #
#                                                                              #
# This file is part of scord.                                                  #
#                                                                              #
# scord is free software: you can redistribute it and/or modify                #
# it under the terms of the GNU General Public License as published by         #
# the Free Software Foundation, either version 3 of the License, or            #
# (at your option) any later version.                                          #
#                                                                              #
# scord is distributed in the hope that it will be useful,                     #
# but WITHOUT ANY WARRANTY; without even the implied warranty of               #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                #
# GNU General Public License for more details.                                 #
#                                                                              #
# You should have received a copy of the GNU General Public License            #
# along with scord.  If not, see <https://www.gnu.org/licenses/>.              #
#                                                                              #
# SPDX-License-Identifier: GPL-3.0-or-later                                    #
################################################################################

add_library(_api_types STATIC)

target_sources(_api_types PUBLIC admire_types.h admire_types.hpp PRIVATE
  types.cpp convert.hpp convert.cpp)

target_include_directories(_api_types PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

target_link_libraries(_api_types PRIVATE
  Margo::Margo common::logger PUBLIC fmt::fmt)

set_property(TARGET _api_types PROPERTY POSITION_INDEPENDENT_CODE ON)

install(
  FILES admire_types.h admire_types.hpp
  DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
+0 −0

File moved.

+0 −168
Changes for src/common/api/admire_types.hpp: 0 added lines, 168 removed lines.
Original line number Diff line number Diff line
@@ -252,177 +252,9 @@ private:
    std::unique_ptr<impl> m_pimpl;
};

template <typename T>
struct managed_rpc_type;

template <typename T>
struct unmanaged_rpc_type;

} // namespace admire


////////////////////////////////////////////////////////////////////////////////
//  Specializations for conversion types
////////////////////////////////////////////////////////////////////////////////

template <>
struct admire::managed_rpc_type<admire::adhoc_storage::ctx> {

    template <typename T, auto Deleter>
    using managed_ptr = scord::utils::ctype_ptr<T, Deleter>;

    explicit managed_rpc_type(const admire::adhoc_storage::ctx& ctx)
        : m_adhoc_context(ADM_adhoc_context_create(
                  static_cast<ADM_adhoc_mode_t>(ctx.exec_mode()),
                  static_cast<ADM_adhoc_access_t>(ctx.access_type()),
                  ctx.nodes(), ctx.walltime(), ctx.should_flush())) {}

    ADM_adhoc_context_t
    get() const {
        return m_adhoc_context.get();
    }

    managed_ptr<ADM_adhoc_context_t, ADM_adhoc_context_destroy> m_adhoc_context;
};


template <>
struct admire::managed_rpc_type<admire::adhoc_storage> {

    template <typename T, auto Deleter>
    using rpc_storage_ptr = scord::utils::ctype_ptr<T, Deleter>;

    template <typename T, auto Deleter>
    using managed_ptr = scord::utils::ctype_ptr<T, Deleter>;

    explicit managed_rpc_type(const admire::adhoc_storage& st)
        : m_adhoc_context(*std::static_pointer_cast<admire::adhoc_storage::ctx>(
                  st.context())),
          m_storage(ADM_storage_create(
                  st.id().c_str(), static_cast<ADM_storage_type_t>(st.type()),
                  m_adhoc_context.get())) {}

    ADM_storage_t
    get() const {
        return m_storage.get();
    }

    managed_rpc_type<admire::adhoc_storage::ctx> m_adhoc_context;
    rpc_storage_ptr<ADM_storage_t, ADM_storage_destroy> m_storage;
};

template <>
struct admire::managed_rpc_type<std::vector<admire::dataset>> {

    template <typename T, auto Deleter>
    using managed_ptr_vector = scord::utils::ctype_ptr_vector<T, Deleter>;

    explicit managed_rpc_type(const std::vector<admire::dataset>& datasets) {
        m_datasets.reserve(datasets.size());

        for(const auto& d : datasets) {
            m_datasets.emplace_back(ADM_dataset_create(d.id().c_str()));
        }
    }

    const ADM_dataset_t*
    data() const {
        return m_datasets.data();
    }

    ADM_dataset_t*
    data() {
        return m_datasets.data();
    }

    std::size_t
    size() const {
        return m_datasets.size();
    }

    managed_ptr_vector<ADM_dataset_t, ADM_dataset_destroy> m_datasets;
};

template <>
struct admire::managed_rpc_type<admire::job_requirements> {

    template <typename T, auto Deleter>
    using rpc_requirements_ptr = scord::utils::ctype_ptr<T, Deleter>;

    explicit managed_rpc_type(const admire::job_requirements& reqs)
        : m_inputs(reqs.inputs()), m_outputs(reqs.outputs()),
          m_storage(*std::dynamic_pointer_cast<admire::adhoc_storage>(
                  reqs.storage())),
          m_reqs(ADM_job_requirements_create(m_inputs.data(), m_inputs.size(),
                                             m_outputs.data(), m_outputs.size(),
                                             m_storage.get())) {}

    ADM_job_requirements_t
    get() const {
        return m_reqs.get();
    }

    managed_rpc_type<std::vector<admire::dataset>> m_inputs;
    managed_rpc_type<std::vector<admire::dataset>> m_outputs;
    managed_rpc_type<admire::adhoc_storage> m_storage;
    rpc_requirements_ptr<ADM_job_requirements_t, ADM_job_requirements_destroy>
            m_reqs;
};

// forward declarations
ADM_job_t
ADM_job_create(uint64_t id);
ADM_return_t
ADM_job_destroy(ADM_job_t job);

template <>
struct admire::managed_rpc_type<admire::job> {

    template <typename T, auto Deleter>
    using rpc_job_ptr = scord::utils::ctype_ptr<T, Deleter>;

    explicit managed_rpc_type(const admire::job& j)
        : m_job(ADM_job_create(j.id())) {}

    ADM_job_t
    get() const {
        return m_job.get();
    }

    rpc_job_ptr<ADM_job_t, ADM_job_destroy> m_job;
};

template <>
struct admire::managed_rpc_type<ADM_job_t> {

    template <typename T, auto Deleter>
    using rpc_job_ptr = scord::utils::ctype_ptr<T, Deleter>;

    explicit managed_rpc_type(ADM_job_t job) : m_job(job) {}

    admire::job
    get() const {
        return admire::job(m_job.get());
    }

    rpc_job_ptr<ADM_job_t, ADM_job_destroy> m_job;
};

template <>
struct admire::unmanaged_rpc_type<admire::job> {

    explicit unmanaged_rpc_type(const admire::job& j)
        : m_job(ADM_job_create(j.id())) {}

    ADM_job_t
    get() const {
        return m_job;
    }

    ADM_job_t m_job;
};


////////////////////////////////////////////////////////////////////////////////
//  Formatting functions
////////////////////////////////////////////////////////////////////////////////
+100 −0
Changes for src/common/api/convert.cpp: 100 added lines, 0 removed lines.
Original line number Diff line number Diff line
/******************************************************************************
 * Copyright 2021-2022, Barcelona Supercomputing Center (BSC), Spain
 *
 * This software was partially supported by the EuroHPC-funded project ADMIRE
 *   (Project ID: 956748, https://www.admire-eurohpc.eu).
 *
 * This file is part of scord.
 *
 * scord is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * scord is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with scord.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *****************************************************************************/

#include <algorithm>
#include "convert.hpp"

// forward declarations
ADM_job_t
ADM_job_create(uint64_t id);

namespace admire::api {

managed_ctype<ADM_adhoc_context_t>
convert(const adhoc_storage::ctx& ctx) {
    return managed_ctype<ADM_adhoc_context_t>{ADM_adhoc_context_create(
            static_cast<ADM_adhoc_mode_t>(ctx.exec_mode()),
            static_cast<ADM_adhoc_access_t>(ctx.access_type()), ctx.nodes(),
            ctx.walltime(), ctx.should_flush())};
}

managed_ctype<ADM_storage_t>
convert(const admire::adhoc_storage& st) {

    auto managed_ctx =
            convert(*std::static_pointer_cast<admire::adhoc_storage::ctx>(
                    st.context()));

    ADM_storage_t c_st = ADM_storage_create(
            st.id().c_str(), static_cast<ADM_storage_type_t>(st.type()),
            managed_ctx.get());

    return managed_ctype<ADM_storage_t>{c_st, std::move(managed_ctx)};
}

managed_ctype_array<ADM_dataset_t>
convert(const std::vector<admire::dataset>& datasets) {

    std::vector<ADM_dataset_t> tmp;

    std::transform(datasets.cbegin(), datasets.cend(), std::back_inserter(tmp),
                   [](const admire::dataset& d) {
                       return ADM_dataset_create(d.id().c_str());
                   });

    return managed_ctype_array<ADM_dataset_t>{std::move(tmp)};
}

managed_ctype<ADM_job_requirements_t>
convert(const admire::job_requirements& reqs) {

    const auto& adhoc_storage =
            *std::dynamic_pointer_cast<admire::adhoc_storage>(reqs.storage());

    auto managed_storage = convert(adhoc_storage);
    auto managed_inputs = convert(reqs.inputs());
    auto managed_outputs = convert(reqs.outputs());

    ADM_job_requirements_t c_reqs = ADM_job_requirements_create(
            managed_inputs.data(), managed_inputs.size(),
            managed_outputs.data(), managed_outputs.size(),
            managed_storage.get());

    return managed_ctype<ADM_job_requirements_t>{
            c_reqs, std::move(managed_inputs), std::move(managed_outputs),
            std::move(managed_storage)};
}


managed_ctype<ADM_job_t>
convert(const job& j) {
    return managed_ctype<ADM_job_t>(ADM_job_create(j.id()));
}

job
convert(ADM_job_t j) {
    return admire::job{j};
}

} // namespace admire::api
+193 −0
Changes for src/common/api/convert.hpp: 193 added lines, 0 removed lines.
Original line number Diff line number Diff line
/******************************************************************************
 * Copyright 2021-2022, Barcelona Supercomputing Center (BSC), Spain
 *
 * This software was partially supported by the EuroHPC-funded project ADMIRE
 *   (Project ID: 956748, https://www.admire-eurohpc.eu).
 *
 * This file is part of scord.
 *
 * scord is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * scord is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with scord.  If not, see <https://www.gnu.org/licenses/>.
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *****************************************************************************/

#ifndef SCORD_CONVERT_HPP
#define SCORD_CONVERT_HPP

#include "admire_types.h"
#include "admire_types.hpp"

namespace admire::api {

// convenience types for managing the types from the C API in a RAII fashion
template <typename T>
struct managed_ctype;

template <typename T>
struct managed_ctype_array;

// conversion functions between C API and CXX API types

managed_ctype<ADM_adhoc_context_t>
convert(const adhoc_storage::ctx& ctx);

managed_ctype<ADM_storage_t>
convert(const admire::adhoc_storage& st);

managed_ctype_array<ADM_dataset_t>
convert(const std::vector<admire::dataset>& datasets);

managed_ctype<ADM_job_requirements_t>
convert(const admire::job_requirements& reqs);

managed_ctype<ADM_job_t>
convert(const job& j);

job
convert(ADM_job_t j);


} // namespace admire::api


////////////////////////////////////////////////////////////////////////////////
//  Specializations for conversion types
////////////////////////////////////////////////////////////////////////////////

template <>
struct admire::api::managed_ctype<ADM_adhoc_context_t> {

    explicit managed_ctype(ADM_adhoc_context_t ctx) : m_adhoc_context(ctx) {}

    ADM_adhoc_context_t
    get() const {
        return m_adhoc_context.get();
    }

    ADM_adhoc_context_t
    release() {
        return m_adhoc_context.release();
    }

    scord::utils::ctype_ptr<ADM_adhoc_context_t, ADM_adhoc_context_destroy>
            m_adhoc_context;
};

template <>
struct admire::api::managed_ctype<ADM_storage_t> {

    explicit managed_ctype(ADM_storage_t st,
                           managed_ctype<ADM_adhoc_context_t>&& ctx)
        : m_storage(st), m_ctx(std::move(ctx)) {}

    ADM_storage_t
    get() const {
        return m_storage.get();
    }

    ADM_storage_t
    release() {
        return m_storage.release();
    }

    scord::utils::ctype_ptr<ADM_storage_t, ADM_storage_destroy> m_storage;
    managed_ctype<ADM_adhoc_context_t> m_ctx;
};

template <>
struct admire::api::managed_ctype_array<ADM_dataset_t> {

    explicit managed_ctype_array(ADM_dataset_t* data, size_t size)
        : m_datasets(data, size) {}

    explicit managed_ctype_array(std::vector<ADM_dataset_t>&& v)
        : m_datasets(v.data(), v.size()) {}

    constexpr size_t
    size() const {
        return m_datasets.size();
    }

    constexpr const ADM_dataset_t*
    data() const noexcept {
        return m_datasets.data();
    }

    constexpr ADM_dataset_t*
    data() noexcept {
        return m_datasets.data();
    }

    constexpr ADM_dataset_t*
    release() noexcept {
        return m_datasets.release();
    }

    scord::utils::ctype_ptr_vector<ADM_dataset_t, ADM_dataset_destroy>
            m_datasets;
};

template <>
struct admire::api::managed_ctype<ADM_job_requirements_t> {

    explicit managed_ctype(ADM_job_requirements_t reqs,
                           managed_ctype_array<ADM_dataset_t>&& inputs,
                           managed_ctype_array<ADM_dataset_t>&& outputs,
                           managed_ctype<ADM_storage_t>&& storage)
        : m_reqs(reqs), m_inputs(std::move(inputs)),
          m_outputs(std::move(outputs)), m_storage(std::move(storage)) {}

    ADM_job_requirements_t
    get() const {
        return m_reqs.get();
    }

    ADM_job_requirements_t
    release() {
        return m_reqs.release();
    }


    scord::utils::ctype_ptr<ADM_job_requirements_t,
                            ADM_job_requirements_destroy>
            m_reqs;
    managed_ctype_array<ADM_dataset_t> m_inputs;
    managed_ctype_array<ADM_dataset_t> m_outputs;
    managed_ctype<ADM_storage_t> m_storage;
};


// forward declarations
ADM_return_t
ADM_job_destroy(ADM_job_t job);

template <>
struct admire::api::managed_ctype<ADM_job_t> {

    explicit managed_ctype(ADM_job_t job) : m_job(job) {}

    ADM_job_t
    get() const {
        return m_job.get();
    }

    ADM_job_t
    release() {
        return m_job.release();
    }

    scord::utils::ctype_ptr<ADM_job_t, ADM_job_destroy> m_job;
};

#endif // SCORD_CONVERT_HPP
Loading
Loading