C++コード設計:JavaにBuilderモード22600;OpenCLカーネルコードコンパイルを参考にする
Builderモード
builderモードとは、Javaコードを設計する際に、メソッド呼び出しのパラメータが多すぎる場合、builderモードですべてのパラメータを1つのクラスにカプセル化し、そのクラスのインスタンスをパラメータとしてメソッドに渡すことを意味します.これにより、メソッドは1つのクラスパラメータを受信するだけで、すべての所望のパラメータを取得することができ、特に複数の類似メソッドに対して、異なるパラメータが必要な場合、この設計はより効率的であり、メソッド呼び出しの複雑さを低減し、エラーの機会を低減することができ、builderモードとは何か分からない場合は、この記事では、Javaメソッドのパラメータが多すぎてどうするか-Part 3-Builderモードについて説明します.Builderモード伝達パラメータはJavaコードに広く適用されています.以下はHttpClientの
Javaに参考する
C++の関数定義はパラメータのデフォルト値を提供することができ、これはJavaより便利な利点であり、そのためJavaよりもいくつかのリロード関数を定義することが少ないが、C++の再構築能力はJavaにはるかに及ばず、同じ関数が複数のリロード関数バージョンを備えている場合、コードメンテナンスの困難はJavaよりも大きい.したがって,この場合JavaのBuilderモードを借りてパラメータをカプセル化する方法は,C++にとってコード収益がより大きくなる.
OpenCLインスタンスの説明
次に、私が最近関わったOpenCL関連の開発作業を例に、私の悩みを話します.
OpenCL開発では、OpenCLデバイス(GPU/CPU)に対してカーネルプログラミング(C 99言語、これは本書で議論する範囲ではない)を行う必要があるので、いくつかのCコードが書かれています.いわゆるkernelコードです.OpenCLデバイス上でkernelを実行するには、まずOpenCLの関数を呼び出してこれらのコードをコンパイルし、それらを実行可能なプログラム(Exceutable Program)にコンパイルします.プログラムでkernelを作成してからkernelを実行するので、OpenCL C++インタフェース(cl.hpp)で
上記のコードから分かるように、
その他のパラメータ、例えばnotifyFptr、data、errは、本プロジェクトでデフォルトのパラメータを使用します.
このうち、カーネルソースコードは文字列であってもローカルファイルから来てもよいので、そのタイプはソースコードを表す文字列であってもよいし、ファイル名を表す文字列であってもよいし、他の実行可能プログラムは1つのソースコードで生成してもよいし、複数のソースコードでコンパイルされた接続で生成してもよいので、ソースコードは複数あってもよい.複数のソースコードの場合は
Preprocessor Options
These options control the OpenCL C preprocessor which is run on each program source before actual compilation.
-D options are processed in the order they are given in the options argument to clBuildProgram or or clCompileProgram.
-D name Predefine name as a macro, with definition 1.
-D name=definition The contents of definition are tokenized and processed as if they appeared during translation phase three in a `#define’ directive. In particular, the definition will be truncated by embedded newline characters.
-I dir Add the directory dir to the list of directories to be searched for header files.
従来の方法では、カーネルソースをコンパイルする関数のセットを提供し、上記の要件を満たすには、次の関数を定義する必要があります.
では、上記の8つの関数の定義を見て、頭が大きい感じがしますか?いずれにしても私は当初こんなに多くの関数を書くのに1日かかりました.もう頭が大きくなりました.各関数は数行しかありませんが、似たような内容が多すぎて、間違いやすくて、メンテナンスが面倒です.もし将来もっと多くのパラメータ(例えば、前に無視したnotifyFptr、data、errパラメータ)を加えるなら、このコードは本当に変更できません.
build_paramパッケージのすべてのパラメータ
おじさんは我慢できません.おばさんは我慢できません.上のコードを書き終わったら、私はもうすぐ崩壊します.翌日、Javaコードを書くときに使ったbuilderモードを思い出しました.上記のコードを書き換えることを決定し、すべてのコンパイルカーネルに必要なパラメータを
buildExecutableProgramは、buildSourceを呼び出すかbuildMultiFilesProgramを呼び出すかをsourceの個数に応じて自動的に決定します.
上の
すべてのパラメータをカプセル化する
builderモードとは、Javaコードを設計する際に、メソッド呼び出しのパラメータが多すぎる場合、builderモードですべてのパラメータを1つのクラスにカプセル化し、そのクラスのインスタンスをパラメータとしてメソッドに渡すことを意味します.これにより、メソッドは1つのクラスパラメータを受信するだけで、すべての所望のパラメータを取得することができ、特に複数の類似メソッドに対して、異なるパラメータが必要な場合、この設計はより効率的であり、メソッド呼び出しの複雑さを低減し、エラーの機会を低減することができ、builderモードとは何か分からない場合は、この記事では、Javaメソッドのパラメータが多すぎてどうするか-Part 3-Builderモードについて説明します.Builderモード伝達パラメータはJavaコードに広く適用されています.以下はHttpClientの
RequestConfig
パラメータクラスのコードです.Httpリクエストに使用される16のパラメータがカプセル化されています.典型的なbuilderモードです.すべてのHttpリクエスト方法では、このクラスのパラメータが使用されます./* * ==================================================================== * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * */
package org.apache.http.client.config;
import java.net.InetAddress;
import java.util.Collection;
import org.apache.http.HttpHost;
import org.apache.http.annotation.Immutable;
/** * Immutable class encapsulating request configuration items. * The default setting for stale connection checking changed * to false, and the feature was deprecated starting with version 4.4. */
@Immutable
public class RequestConfig implements Cloneable {
public static final RequestConfig DEFAULT = new Builder().build();
private final boolean expectContinueEnabled;
private final HttpHost proxy;
private final InetAddress localAddress;
private final boolean staleConnectionCheckEnabled;
private final String cookieSpec;
private final boolean redirectsEnabled;
private final boolean relativeRedirectsAllowed;
private final boolean circularRedirectsAllowed;
private final int maxRedirects;
private final boolean authenticationEnabled;
private final Collection<String> targetPreferredAuthSchemes;
private final Collection<String> proxyPreferredAuthSchemes;
private final int connectionRequestTimeout;
private final int connectTimeout;
private final int socketTimeout;
private final boolean decompressionEnabled;
RequestConfig(
final boolean expectContinueEnabled,
final HttpHost proxy,
final InetAddress localAddress,
final boolean staleConnectionCheckEnabled,
final String cookieSpec,
final boolean redirectsEnabled,
final boolean relativeRedirectsAllowed,
final boolean circularRedirectsAllowed,
final int maxRedirects,
final boolean authenticationEnabled,
final Collection<String> targetPreferredAuthSchemes,
final Collection<String> proxyPreferredAuthSchemes,
final int connectionRequestTimeout,
final int connectTimeout,
final int socketTimeout,
final boolean decompressionEnabled) {
super();
this.expectContinueEnabled = expectContinueEnabled;
this.proxy = proxy;
this.localAddress = localAddress;
this.staleConnectionCheckEnabled = staleConnectionCheckEnabled;
this.cookieSpec = cookieSpec;
this.redirectsEnabled = redirectsEnabled;
this.relativeRedirectsAllowed = relativeRedirectsAllowed;
this.circularRedirectsAllowed = circularRedirectsAllowed;
this.maxRedirects = maxRedirects;
this.authenticationEnabled = authenticationEnabled;
this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
this.connectionRequestTimeout = connectionRequestTimeout;
this.connectTimeout = connectTimeout;
this.socketTimeout = socketTimeout;
this.decompressionEnabled = decompressionEnabled;
}
/** * Determines whether the 'Expect: 100-Continue' handshake is enabled * for entity enclosing methods. The purpose of the 'Expect: 100-Continue' * handshake is to allow a client that is sending a request message with * a request body to determine if the origin server is willing to * accept the request (based on the request headers) before the client * sends the request body. * <p> * The use of the 'Expect: 100-continue' handshake can result in * a noticeable performance improvement for entity enclosing requests * (such as POST and PUT) that require the target server's * authentication. * </p> * <p> * 'Expect: 100-continue' handshake should be used with caution, as it * may cause problems with HTTP servers and proxies that do not support * HTTP/1.1 protocol. * </p> * <p> * Default: {@code false} * </p> */
public boolean isExpectContinueEnabled() {
return expectContinueEnabled;
}
/** * Returns HTTP proxy to be used for request execution. * <p> * Default: {@code null} * </p> */
public HttpHost getProxy() {
return proxy;
}
/** * Returns local address to be used for request execution. * <p> * On machines with multiple network interfaces, this parameter * can be used to select the network interface from which the * connection originates. * </p> * <p> * Default: {@code null} * </p> */
public InetAddress getLocalAddress() {
return localAddress;
}
/** * Determines whether stale connection check is to be used. The stale * connection check can cause up to 30 millisecond overhead per request and * should be used only when appropriate. For performance critical * operations this check should be disabled. * <p> * Default: {@code false} since 4.4 * </p> * * @deprecated (4.4) Use {@link * org.apache.http.impl.conn.PoolingHttpClientConnectionManager#getValidateAfterInactivity()} */
@Deprecated
public boolean isStaleConnectionCheckEnabled() {
return staleConnectionCheckEnabled;
}
/** * Determines the name of the cookie specification to be used for HTTP state * management. * <p> * Default: {@code null} * </p> */
public String getCookieSpec() {
return cookieSpec;
}
/** * Determines whether redirects should be handled automatically. * <p> * Default: {@code true} * </p> */
public boolean isRedirectsEnabled() {
return redirectsEnabled;
}
/** * Determines whether relative redirects should be rejected. HTTP specification * requires the location value be an absolute URI. * <p> * Default: {@code true} * </p> */
public boolean isRelativeRedirectsAllowed() {
return relativeRedirectsAllowed;
}
/** * Determines whether circular redirects (redirects to the same location) should * be allowed. The HTTP spec is not sufficiently clear whether circular redirects * are permitted, therefore optionally they can be enabled * <p> * Default: {@code false} * </p> */
public boolean isCircularRedirectsAllowed() {
return circularRedirectsAllowed;
}
/** * Returns the maximum number of redirects to be followed. The limit on number * of redirects is intended to prevent infinite loops. * <p> * Default: {@code 50} * </p> */
public int getMaxRedirects() {
return maxRedirects;
}
/** * Determines whether authentication should be handled automatically. * <p> * Default: {@code true} * </p> */
public boolean isAuthenticationEnabled() {
return authenticationEnabled;
}
/** * Determines the order of preference for supported authentication schemes * when authenticating with the target host. * <p> * Default: {@code null} * </p> */
public Collection<String> getTargetPreferredAuthSchemes() {
return targetPreferredAuthSchemes;
}
/** * Determines the order of preference for supported authentication schemes * when authenticating with the proxy host. * <p> * Default: {@code null} * </p> */
public Collection<String> getProxyPreferredAuthSchemes() {
return proxyPreferredAuthSchemes;
}
/** * Returns the timeout in milliseconds used when requesting a connection * from the connection manager. A timeout value of zero is interpreted * as an infinite timeout. * <p> * A timeout value of zero is interpreted as an infinite timeout. * A negative value is interpreted as undefined (system default). * </p> * <p> * Default: {@code -1} * </p> */
public int getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
/** * Determines the timeout in milliseconds until a connection is established. * A timeout value of zero is interpreted as an infinite timeout. * <p> * A timeout value of zero is interpreted as an infinite timeout. * A negative value is interpreted as undefined (system default). * </p> * <p> * Default: {@code -1} * </p> */
public int getConnectTimeout() {
return connectTimeout;
}
/** * Defines the socket timeout ({@code SO_TIMEOUT}) in milliseconds, * which is the timeout for waiting for data or, put differently, * a maximum period inactivity between two consecutive data packets). * <p> * A timeout value of zero is interpreted as an infinite timeout. * A negative value is interpreted as undefined (system default). * </p> * <p> * Default: {@code -1} * </p> */
public int getSocketTimeout() {
return socketTimeout;
}
/** * Determines whether compressed entities should be decompressed automatically. * <p> * Default: {@code true} * </p> * * @since 4.4 */
public boolean isDecompressionEnabled() {
return decompressionEnabled;
}
@Override
protected RequestConfig clone() throws CloneNotSupportedException {
return (RequestConfig) super.clone();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append("expectContinueEnabled=").append(expectContinueEnabled);
builder.append(", proxy=").append(proxy);
builder.append(", localAddress=").append(localAddress);
builder.append(", cookieSpec=").append(cookieSpec);
builder.append(", redirectsEnabled=").append(redirectsEnabled);
builder.append(", relativeRedirectsAllowed=").append(relativeRedirectsAllowed);
builder.append(", maxRedirects=").append(maxRedirects);
builder.append(", circularRedirectsAllowed=").append(circularRedirectsAllowed);
builder.append(", authenticationEnabled=").append(authenticationEnabled);
builder.append(", targetPreferredAuthSchemes=").append(targetPreferredAuthSchemes);
builder.append(", proxyPreferredAuthSchemes=").append(proxyPreferredAuthSchemes);
builder.append(", connectionRequestTimeout=").append(connectionRequestTimeout);
builder.append(", connectTimeout=").append(connectTimeout);
builder.append(", socketTimeout=").append(socketTimeout);
builder.append(", decompressionEnabled=").append(decompressionEnabled);
builder.append("]");
return builder.toString();
}
public static RequestConfig.Builder custom() {
return new Builder();
}
@SuppressWarnings("deprecation")
public static RequestConfig.Builder copy(final RequestConfig config) {
return new Builder()
.setExpectContinueEnabled(config.isExpectContinueEnabled())
.setProxy(config.getProxy())
.setLocalAddress(config.getLocalAddress())
.setStaleConnectionCheckEnabled(config.isStaleConnectionCheckEnabled())
.setCookieSpec(config.getCookieSpec())
.setRedirectsEnabled(config.isRedirectsEnabled())
.setRelativeRedirectsAllowed(config.isRelativeRedirectsAllowed())
.setCircularRedirectsAllowed(config.isCircularRedirectsAllowed())
.setMaxRedirects(config.getMaxRedirects())
.setAuthenticationEnabled(config.isAuthenticationEnabled())
.setTargetPreferredAuthSchemes(config.getTargetPreferredAuthSchemes())
.setProxyPreferredAuthSchemes(config.getProxyPreferredAuthSchemes())
.setConnectionRequestTimeout(config.getConnectionRequestTimeout())
.setConnectTimeout(config.getConnectTimeout())
.setSocketTimeout(config.getSocketTimeout())
.setDecompressionEnabled(config.isDecompressionEnabled());
}
public static class Builder {
private boolean expectContinueEnabled;
private HttpHost proxy;
private InetAddress localAddress;
private boolean staleConnectionCheckEnabled;
private String cookieSpec;
private boolean redirectsEnabled;
private boolean relativeRedirectsAllowed;
private boolean circularRedirectsAllowed;
private int maxRedirects;
private boolean authenticationEnabled;
private Collection<String> targetPreferredAuthSchemes;
private Collection<String> proxyPreferredAuthSchemes;
private int connectionRequestTimeout;
private int connectTimeout;
private int socketTimeout;
private boolean decompressionEnabled;
Builder() {
super();
this.staleConnectionCheckEnabled = false;
this.redirectsEnabled = true;
this.maxRedirects = 50;
this.relativeRedirectsAllowed = true;
this.authenticationEnabled = true;
this.connectionRequestTimeout = -1;
this.connectTimeout = -1;
this.socketTimeout = -1;
this.decompressionEnabled = true;
}
public Builder setExpectContinueEnabled(final boolean expectContinueEnabled) {
this.expectContinueEnabled = expectContinueEnabled;
return this;
}
public Builder setProxy(final HttpHost proxy) {
this.proxy = proxy;
return this;
}
public Builder setLocalAddress(final InetAddress localAddress) {
this.localAddress = localAddress;
return this;
}
/** * @deprecated (4.4) Use {@link * org.apache.http.impl.conn.PoolingHttpClientConnectionManager#setValidateAfterInactivity(int)} */
@Deprecated
public Builder setStaleConnectionCheckEnabled(final boolean staleConnectionCheckEnabled) {
this.staleConnectionCheckEnabled = staleConnectionCheckEnabled;
return this;
}
public Builder setCookieSpec(final String cookieSpec) {
this.cookieSpec = cookieSpec;
return this;
}
public Builder setRedirectsEnabled(final boolean redirectsEnabled) {
this.redirectsEnabled = redirectsEnabled;
return this;
}
public Builder setRelativeRedirectsAllowed(final boolean relativeRedirectsAllowed) {
this.relativeRedirectsAllowed = relativeRedirectsAllowed;
return this;
}
public Builder setCircularRedirectsAllowed(final boolean circularRedirectsAllowed) {
this.circularRedirectsAllowed = circularRedirectsAllowed;
return this;
}
public Builder setMaxRedirects(final int maxRedirects) {
this.maxRedirects = maxRedirects;
return this;
}
public Builder setAuthenticationEnabled(final boolean authenticationEnabled) {
this.authenticationEnabled = authenticationEnabled;
return this;
}
public Builder setTargetPreferredAuthSchemes(final Collection<String> targetPreferredAuthSchemes) {
this.targetPreferredAuthSchemes = targetPreferredAuthSchemes;
return this;
}
public Builder setProxyPreferredAuthSchemes(final Collection<String> proxyPreferredAuthSchemes) {
this.proxyPreferredAuthSchemes = proxyPreferredAuthSchemes;
return this;
}
public Builder setConnectionRequestTimeout(final int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
return this;
}
public Builder setConnectTimeout(final int connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
public Builder setSocketTimeout(final int socketTimeout) {
this.socketTimeout = socketTimeout;
return this;
}
public Builder setDecompressionEnabled(final boolean decompressionEnabled) {
this.decompressionEnabled = decompressionEnabled;
return this;
}
public RequestConfig build() {
return new RequestConfig(
expectContinueEnabled,
proxy,
localAddress,
staleConnectionCheckEnabled,
cookieSpec,
redirectsEnabled,
relativeRedirectsAllowed,
circularRedirectsAllowed,
maxRedirects,
authenticationEnabled,
targetPreferredAuthSchemes,
proxyPreferredAuthSchemes,
connectionRequestTimeout,
connectTimeout,
socketTimeout,
decompressionEnabled);
}
}
}
Javaに参考する
C++の関数定義はパラメータのデフォルト値を提供することができ、これはJavaより便利な利点であり、そのためJavaよりもいくつかのリロード関数を定義することが少ないが、C++の再構築能力はJavaにはるかに及ばず、同じ関数が複数のリロード関数バージョンを備えている場合、コードメンテナンスの困難はJavaよりも大きい.したがって,この場合JavaのBuilderモードを借りてパラメータをカプセル化する方法は,C++にとってコード収益がより大きくなる.
OpenCLインスタンスの説明
次に、私が最近関わったOpenCL関連の開発作業を例に、私の悩みを話します.
OpenCL開発では、OpenCLデバイス(GPU/CPU)に対してカーネルプログラミング(C 99言語、これは本書で議論する範囲ではない)を行う必要があるので、いくつかのCコードが書かれています.いわゆるkernelコードです.OpenCLデバイス上でkernelを実行するには、まずOpenCLの関数を呼び出してこれらのコードをコンパイルし、それらを実行可能なプログラム(Exceutable Program)にコンパイルします.プログラムでkernelを作成してからkernelを実行するので、OpenCL C++インタフェース(cl.hpp)で
cl::Program,cl::Kernel
クラスを定義してホストプラットフォーム(windows/linux....)上の开発の経験、私达は知っていて、1つのC/C++コードをターゲットファイル(exe ORダイナミックライブラリ)にコンパイルするには、complie、linkの2つの段阶を経なければならなくて、complieの段阶はすべてのC/C++をobjにコンパイルして、linkの段阶はすべてのobjを接続してターゲットファイルを生成して、実はkernelをコンパイルするのも同じです.したがって、cl::Program
に対応するbuild,complie
関数と、cl::linkProgram
関数とがあり、build
関数にはcomplie/linkが含まれており、単一のソースコードを実行可能なプログラムにコンパイルするために使用される.次に、cl::Program
クラスの主な構造関数とメソッドの定義(cl.hppから抜粋)を示す.//cl::Program
Program(const STRING_CLASS& source,bool build = false, cl_int* err = NULL);
//cl::Program
Program(const Context& context,const STRING_CLASS& source,bool build = false,cl_int* err = NULL);
//cl::Program
Program(const Context& context,const Sources& sources,cl_int* err = NULL)
//cl::Program source (Executable Program)
cl_int build(
const VECTOR_CLASS<Device>& devices,
const char* options = NULL,
void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
void* data = NULL) const;
//cl::Program source obj ( Program) link
cl_int compile(
const char* options = NULL,
void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
void* data = NULL) const;
// cl::Program (Executable Program)
inline Program linkProgram(
Program input1,
Program input2,
const char* options = NULL,
void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
void* data = NULL,
cl_int* err = NULL);
// link cl::Program (Executable Program)
inline Program linkProgram(
VECTOR_CLASS<Program> inputPrograms,
const char* options = NULL,
void (CL_CALLBACK * notifyFptr)(cl_program, void *) = NULL,
void* data = NULL,
cl_int* err = NULL)
上記のコードから分かるように、
cl::Program
を構築し、Exceutable Programにコンパイルする主な基本パラメータは、次のとおりです.const Context& context // ,
const VECTOR_CLASS<Device>& devices // ,
const STRING_CLASS& source //
const STRING_CLASS& source_name // , , "Unknow_name"
const char* options // nullptr
その他のパラメータ、例えばnotifyFptr、data、errは、本プロジェクトでデフォルトのパラメータを使用します.
このうち、カーネルソースコードは文字列であってもローカルファイルから来てもよいので、そのタイプはソースコードを表す文字列であってもよいし、ファイル名を表す文字列であってもよいし、他の実行可能プログラムは1つのソースコードで生成してもよいし、複数のソースコードでコンパイルされた接続で生成してもよいので、ソースコードは複数あってもよい.複数のソースコードの場合は
std::vector<std::string>
でデバイスオブジェクトリストの許可なしを記述する必要があるため、デフォルトパラメータコンパイルオプションが必要で提供しないことを許可する必要があるため、デフォルトパラメータカーネルコードコンパイルが必要な場合、多くのコンパイルオプションもあるが、2つの最も基本的なコンパイルオプション-D-I-Iは、ソースコードにincludeの他のファイルがある場合、optionsで-Iで#includeファイルを指定する必要がある検索パス-Dは、カーネルソースコードのマクロ定義を提供します.opencl公式サイト:clBuildProgramPreprocessor Options
These options control the OpenCL C preprocessor which is run on each program source before actual compilation.
-D options are processed in the order they are given in the options argument to clBuildProgram or or clCompileProgram.
-D name Predefine name as a macro, with definition 1.
-D name=definition The contents of definition are tokenized and processed as if they appeared during translation phase three in a `#define’ directive. In particular, the definition will be truncated by embedded newline characters.
-I dir Add the directory dir to the list of directories to be searched for header files.
従来の方法では、カーネルソースをコンパイルする関数のセットを提供し、上記の要件を満たすには、次の関数を定義する必要があります.
////// cl::Program////////////////////////
cl::Program createProgram(const cl::Context& context,
const std::string &source,
const std::string& source_name=Unknow_Name,// ,Unknow_Name
const char* options=nullptr,
const std::vector<cl::Device>& devices=Empty_Devices_Vector //Empty_Devices_Vector
);
cl::Program createProgram(const cl::Context& context,
const char*file,
const std::string& source_name=Unknow_Name,
const char* options=nullptr,
const std::vector<cl::Device>& devices=Empty_Devices_Vector);
////// cl::Program////////////////////////
cl::Program createProgram(const cl::Context& context,
const std::vector<std::pair<std::string,std::string>> &sources, // ,vector std::pair ,pair.first ,pairt.second
const char* options=nullptr,
const std::vector<cl::Device>& devices=Empty_Devices_Vector);
cl::Program createProgram(const cl::Context& context,
const std::vector<cl::Device>& devices,
const std::vector<std::string> &source_files, // , source_name
const char* options=nullptr,
const std::vector<cl::Device>& devices=Empty_Devices_Vector);
///////////// 4 source_root,define_str,include_str ////////////
////// cl::Program////////////////////////
cl::Program createProgram(const cl::Context& context,
const std::string &source,
const std::string& source_name=Unknow_Name,// ,Unknow_Name
const char* define_str =nullptr, // -D
const char* include_str =nullptr,// -I
const char* other_options=nullptr,//
const std::vector<cl::Device>& devices=Empty_Devices_Vector //Empty_Devices_Vector
);
cl::Program createProgram(const cl::Context& context,
const char*file,
const std::string& source_name=Unknow_Name,
const char* define_str =nullptr, // -D
const char* include_str =nullptr,// -I
const char* other_options=nullptr,//
const std::vector<cl::Device>& devices=Empty_Devices_Vector);
////// cl::Program////////////////////////
cl::Program createProgram(const cl::Context& context,
const std::vector<std::pair<std::string,std::string>> &sources, // ,vector std::pair ,pair.first ,pairt.second
const char* source_root=nullptr,//
const char* define_str =nullptr, // -D
const char* include_str =nullptr,// -I
const char* other_options=nullptr,//
const std::vector<cl::Device>& devices=Empty_Devices_Vector);
cl::Program createProgram(const cl::Context& context,
const std::vector<cl::Device>& devices,
const std::vector<std::string> &source_files, // , source_name
const char* source_root=nullptr,//
const char* define_str =nullptr, // -D
const char* include_str =nullptr,// -I
const char* other_options=nullptr,//
const std::vector<cl::Device>& devices=Empty_Devices_Vector);
では、上記の8つの関数の定義を見て、頭が大きい感じがしますか?いずれにしても私は当初こんなに多くの関数を書くのに1日かかりました.もう頭が大きくなりました.各関数は数行しかありませんが、似たような内容が多すぎて、間違いやすくて、メンテナンスが面倒です.もし将来もっと多くのパラメータ(例えば、前に無視したnotifyFptr、data、errパラメータ)を加えるなら、このコードは本当に変更できません.
build_paramパッケージのすべてのパラメータ
おじさんは我慢できません.おばさんは我慢できません.上のコードを書き終わったら、私はもうすぐ崩壊します.翌日、Javaコードを書くときに使ったbuilderモードを思い出しました.上記のコードを書き換えることを決定し、すべてのコンパイルカーネルに必要なパラメータを
build_param
クラスにカプセル化する./* */
struct build_param{
// pair.first ,pairt.second
using source_info_type =std::pair<std::string,std::string>;
cl::Context context; //
std::vector<cl::Device> devices; //
std::vector<source_info_type> sources; //
std::string options; //
class builder{
private:
const cl::Context _context;
std::vector<cl::Device> _devices;
std::vector<source_info_type> _sources;
std::string _source_root=Empty_String;
std::vector<std::string> _source_files;
std::string _options=Empty_String;
public:
// builder
builder(const cl::Context &context) :_context(context) {}
// _devices ,
builder &set_devices(std::initializer_list<cl::Device>devices){
this->_devices=devices;
return *this;
}
// _devices ,
builder &set_devices(const std::vector<cl::Device>&devices){
this->_devices=devices;
return *this;
}
//
builder &add_sources(std::initializer_list<source_info_type>sources){
this->_sources.insert(this->_sources.end(),sources);
return *this;
}
//
builder &add_source(const source_info_type &source){
return add_sources({source});
}
// ,source_name
builder &add_source(const std::string &source,const std::string &source_name=Unknow_Name){
return add_source({source,source_name});
}
/* * add_include #include * */
builder &set_source_root(const std::string &root){
if(_source_root.empty()&&!root.empty()){
_source_root=root;
add_include(root);
}
return *this;
}
/* source */
builder &add_source_files(std::initializer_list<std::string>sources){
this->_source_files.insert(this->_source_files.end(),sources);
return *this;
}
/* source */
builder &add_source_file(const std::string &source) {
return add_source_files({source});
}
/* -D */
builder &add_define(std::string def){
throw_if(def.empty(),"def is empty")
_options+="-D "+def+" ";
return *this;
}
/* -I */
builder &add_include(std::string dir){
auto p=gdface::trim(dir);
throw_if(p.empty(),"dir is empty")
if(gdface::has_space(p))
p="\""+p+"\"";
_options+="-I "+p+" ";
return *this;
}
/* */
builder &add_options(std::string opt){
if(!opt.empty())
_options+=opt+" ";
return *this;
}
/* build_param */
build_param build(){
if(_source_root.empty()){
add_include(gdface::getcwd()); // #include
}
// std::string
for(auto file:_source_files){
throw_if(file.empty(),"the argument 'file' is empty")
this->_sources.emplace_back(file,gdface::load_string(gdface::path_concate(_source_root,file).data()));
}
// build_param
return {_context,_devices,_sources,_options};
}
};
/* cl::Context builder */
static builder custom(const cl::Context &context){
return builder(context);
}
//
build_param() = default;
//
build_param(const build_param &)=default;
//
build_param(build_param &&)=default;
//
build_param&operator=(const build_param &)=default;
build_param(const cl::Context &context,
const std::vector<cl::Device> &devices,
const std::vector<source_info_type> &sources,
const std::string options):context(context),devices(devices),sources(sources),options(options){}
};
build_param
クラスがあれば,カーネルプログラムをコンパイルする関数定義は以下のように簡略化される./* ( ) */
cl::Program buildExecutableProgram(const build_param& param){
// 1 , buildSource
if(1==param.sources.size()){
return buildSource({param.context,param.devices,{param.sources[0]},param.options});
}
return buildMultiFilesProgram(param);
}
buildExecutableProgramは、buildSourceを呼び出すかbuildMultiFilesProgramを呼び出すかをsourceの個数に応じて自動的に決定します.
上の
buildSource
,buildMultiFilesProgram
関数の実装はこのように滴下され、/* , complie_only obj */
cl::Program buildSource(const build_param& param, bool complie_only = false) {
try {
throw_if(1 != param.sources.size(), "size of build_param::sources must be 1")
cl::Program program(param.context, param.sources[0].second);
try {
#ifdef CL_VERSION_1_2
if (complie_only) {
show_on_buildstart(_DEF_STRING(complie), param.options.data(), param.sources[0]);
program.compile(param.options.data());
} else
#else
throw_exception_if(face_cl_build_exception,complie_only,"unsupported complie under version OpenCL 1.2")
#endif
{
show_on_buildstart(_DEF_STRING(build), param.options.data(), param.sources[0]);
if (param.devices.size())
program.build(param.devices, param.options.data());
else
program.build(param.options.data());
}
show_on_buildend(program, param.sources[0]);
return std::move(program);
}
#ifdef CL_VERSION_2_0
// OpenCL 2.0 , cl::BuildError
catch (cl::BuildError &e) {
auto log = e.getBuildLog();
showBuildLog(log, param.sources[0].first);
throw face_cl_build_exception(SOURCE_AT, e);
}
#else
// OpenCL 1.1,1.2 , cl::Error
catch (cl::Error& e) {
auto log = cl_utilits::getBuildInfo<CL_PROGRAM_BUILD_LOG>(program);
showBuildLog(log, param.sources[0].first);
throw face_cl_build_exception(SOURCE_AT, e, log);
}
#endif
} catch (cl::Error& e) {
throw face_cl_build_exception(SOURCE_AT, e);
}
}
/* */
cl::Program cl_utilits::buildMultiFilesProgram(const build_param& param) {
// 0
throw_if(0==param.sources.size(),"size of build_param::sources must not be 0")
std::vector<cl::Program> objs;
// obj
for (auto source : param.sources) {
objs.emplace_back(compileSource({ param.context,param.devices,{ source },param.options }));
}
return cl::linkProgram(objs, param.options.data());// obj
}
すべてのパラメータをカプセル化する
build_param
があれば、将来、より多くのパラメータを追加しても、build_param
と関連する関数に対応するコードを追加するだけで、すべての関数インタフェースの定義を変更する必要がなく、メンテナンス性も向上します.