Collaborate with AI -- Write a modern C++ singleton factory

translate  my blog <<与AI合作 -- 写一个modern c++单例工厂>> to English.

=========================================================================

NOTE: It was written at 2024.01, maybe the AI is not smart as now.

Preface


In this article, readers can learn about a hybrid of the modern C++ singleton pattern and factory pattern. At the same time, you will gain insights into:

  • How intelligent AI has become today,
  • How to leverage AI to quickly turn ideas into reality, and
  • Whether such a level of intelligence might make programmers lose their jobs?

Question
This time, I won't use ChatGPT; instead, I've switched to Bard.

mzhai: Write a Modern c++ program to implement a singleton factory to produce 2 products. Each product has different arguments, the 1st has only one parameter of type int, the 2nd product has two parameters int and string respectively.

 Here, I specifically requested creating two products with different parameters to ensure that the classes generated by AI are versatile.

The answer given by bard

  1 #include <iostream>
  2 #include <memory>
  3 #include <utility>
  4
  5 enum class ProductType {
  6     Product1,
  7     Product2
  8 };
  9
 10 class Product {
 11 public:
 12     virtual ~Product() = default;
 13     virtual void use() = 0;
 14 };
 15
 16 // Concrete products with different arguments
 17 class Product1 : public Product {
 18 public:
 19     Product1(int arg1) : arg1_(arg1) {}
 20     void use() override { std::cout << "Using Product1 with arg1: " << arg1_ << std::endl; }
 21 private:
 22     int arg1_;
 23 };
 24
 25 class Product2 : public Product {
 26 public:
 27     Product2(int arg2_1, std::string arg2_2) : arg2_1_(arg2_1), arg2_2_(arg2_2) {}
 28     void use() override { std::cout << "Using Product2 with arg2_1: " << arg2_1_ << ", arg2_2: " << arg2_2_ << std::endl;     }
 29 private:
 30     int arg2_1_;
 31     std::string arg2_2_;
 32 };
 33
 34 // Singleton factory with modern techniques
 35 class ProductFactory {
 36 public:
 37     static ProductFactory& getInstance() {
 38         static ProductFactory instance;
 39         return instance;
 40     }
 41
 42     template <typename... Args>
 43     std::unique_ptr<Product> createProduct(ProductType type, Args&&... args) {
 44         switch (type) {
 45             case ProductType::Product1:
 46                 return std::make_unique<Product1>(std::forward<Args>(args)...);
 47             case ProductType::Product2:
 48                 return std::make_unique<Product2>(std::forward<Args>(args)...);
 49             default:
 50                 throw std::invalid_argument("Invalid product type");
 51         }
 52     }
 53
 54 private:
 55     ProductFactory() = default; // Private constructor for singleton
 56 };
 57
 58 int main() {
 59     ProductFactory& factory = ProductFactory::getInstance();
 60
 61     std::unique_ptr<Product> product1 = factory.createProduct(ProductType::Product1, 42);
 62     std::unique_ptr<Product> product2 = factory.createProduct(ProductType::Product2, 5, "Hello");
 63
 64     product1->use();
 65     product2->use();
 66
 67     return 0;
 68 }

The problem with AI's answers

It fails with compilation errors, the error message is listed as below:

/usr/bin/../lib/gcc/x86_64-linux-gnu/12/../../../../include/c++/12/bits/unique_ptr.h:1065:34: error: no matching constructor for initialization of 'Product2'
 1065 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                                  ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/insights/insights.cpp:48:29: note: in instantiation of function template specialization 'std::make_unique<Product2, int>' requested here
   48 |                 return std::make_unique<Product2>(std::forward<Args>(args)...);
      |                             ^

It’s a bit strange, right? On line 62, we clearly passed two parameters to Product2. Doesn’t it only go to case ProductType::Product2 (line 48) when Product2 is involved? However, the compiler complains that only one int was passed to the constructor of Product2. Why?

This is because case statements are runtime code, while createProduct generates two overloads respectively based on lines 61 and 62, each containing all the code of createProduct body. For example, line 61 would generate the following code:

std::unique_ptr<Product> createProduct(ProductType type, int arg) {
    switch (type) {
        case ProductType::Product1:
            return std::make_unique<Product1>(arg);
        case ProductType::Product2:
            return std::make_unique<Product2>(arg);
        default:
            throw std::invalid_argument("Invalid product type");
    }
}

The generated code std::make_unique<Product2>(arg) doesn't match the unique one constructor Product2(int arg2_1, std::string arg2_2) .

Ask Bard to make improvement

Directly pasting the error to Bard, it still failed to truly understand the reason for the compilation error. The reason I say this is the solution it provided was:

case ProductType::Product2:
                if constexpr (sizeof...(Args) == 2) { // Ensure 2 arguments for Product2
                    return std::make_unique<Product2>(std::forward<Args>(args)...);
                } else {
                    throw std::invalid_argument("Invalid arguments for Product2");
                }

constexpr (sizeof...(Args) == 2) does indeed evaluate to TRUE/FALSE at compile time: if the condition is not met, this piece of code will be skipped. As a result, the createProduct function instantiated on line 61 will not produce any error. Let’s try this approach for now, even though it is ugly(which will be discussed later). 

  1 #include <iostream>
  2 #include <memory>
  3 #include <utility>
  4
  5 class Product {
  6 public:
  7     virtual ~Product() = default;
  8     virtual void use() = 0;
  9 };
 10
 11 // Concrete products with different arguments
 12 class Product1 : public Product {
 13 public:
 14     Product1(int arg1) : arg1_(arg1) {}
 15     void use() override { std::cout << "Using Product1 with arg1: " << arg1_ << std::endl; }
 16 private:
 17     int arg1_;
 18 };
 19
 20 class Product2 : public Product {
 21 public:
 22     Product2(int arg2_1, std::string arg2_2) : arg2_1_(arg2_1), arg2_2_(arg2_2) {}
 23     void use() override { std::cout << "Using Product2 with arg2_1: " << arg2_1_ << ", arg2_2: " << arg2_2_ << std::endl;     }
 24 private:
 25     int arg2_1_;
 26     std::string arg2_2_;
 27 };
 28
 29 class ProductFactory {
 30 public:
 31     static ProductFactory& getInstance() {
 32         static ProductFactory instance;
 33         return instance;
 34     }
 35     template <typename... Args>
 36     std::unique_ptr<Product> createProduct(Args&&... args) {
 37         if constexpr (sizeof...(Args) == 1){
 38             return std::make_unique<Product1>(std::forward<Args>(args)...);
 39         }
 40         if constexpr (sizeof...(Args) == 2){
 41             return std::make_unique<Product2>(std::forward<Args>(args)...);
 42         }
 43     }
 44 private:
 45     ProductFactory() = default; // Private constructor for singleton
 46 };
 47
 48 int main() {
 49     ProductFactory& factory = ProductFactory::getInstance();
 50
 51     std::unique_ptr<Product> product1 = factory.createProduct(42);
 52     std::string hi("Hello");
 53     std::unique_ptr<Product> product2 = factory.createProduct(5, hi);
 54
 55     product1->use();
 56     product2->use();
 57
 58     return 0;
 59 }

Compile and run, both no problem. Moreover, let's dig deeply a little bit by cppinsights.io to look at the two instatiated functions:

 Obviously, this approach is not ideal:

  1. Think about it—what if both Product1 and Product2 each had only one parameter? In that case, sizeof would no longer be able to differentiate them.
  2. Moreover, this method generates a lot of redundant code (if constexpr...).

Back to human being

So, let me quickly fix it myself. We can pass an additional template parameter to the createProduct function, for example, called T, like this:

template <typename T, typename... Args>
std::unique_ptr<T> createProduct(Args&&... args) {
    return std::make_unique<T>(std::forward<Args>(args)...);
}

...

std::unique_ptr<Product> product1 = factory.createProduct<Product1>(42);
std::string hi("Hello");
std::unique_ptr<Product> product2 = factory.createProduct<Product2>(5, hi);

Let's look at how createProduct being instantiated.

 Ask Bard how about this improvement? It says:

mzhai:How about changing the code to above?

Bard: I'm happy to review the proposed code changes:

Key Changes:

  • Templated createProduct function: The createProduct function now takes a template parameter T to specify the product type, eliminating the need for the ProductType enum. 
  • Removal of switch statement: The switch statement is no longer needed as the compiler determines the correct type based on the template argument.
  • Explicit product type in main: The createProduct function is called with explicit template arguments (Product1 and Product2) to create the desired product types.

Analysis:

  • Pros:
    • More concise and less verbose code. 
    • Leverages template metaprogramming for type-driven behavior.
    • Potentially easier to extend for new product types.
  • Cons:
    • Might be less readable for those less familiar with templates.
    • Error messages might be more cryptic due to template instantiation.
    • Doesn't enforce a closed set of product types (could potentially create other types).

Overall:

  • The changes are valid and adhere to modern C++ practices.
  • The choice between this approach and the previous one using the ProductType enum is mainly a matter of preference and trade-offs.
  • Consider the team's familiarity with templates and the specific project requirements when deciding on the best approach.

Additional Notes:

  • Ensure that all product classes (Product1Product2, etc.) are derived from the Product base class for this approach to work correctly.
  • Consider using static_assert or concepts (if available in your C++ version) to enforce constraints on the template parameter T, ensuring it's a valid product type.

AI VS Human being

AI is indeed still limited in intelligence, but it can undeniably improve programming efficiency. Tools like Copilot are often advertised as being able to boost human programming efficiency by around 50%. Does this mean that, without new job opportunities being created, half of the engineers will lose their jobs?

Final code

You can copy below code and do expeirments.

#include <iostream>
#include <memory>
#include <utility>

class Product {
public:
    virtual ~Product() = default;
    virtual void use() = 0;
};

// Concrete products with different arguments
class Product1 : public Product {
public:
    Product1(int arg1) : arg1_(arg1) {}
    void use() override { std::cout << "Using Product1 with arg1: " << arg1_ << std::endl; }
private:
    int arg1_;
};

class Product2 : public Product {
public:
    Product2(int arg2_1, std::string arg2_2) : arg2_1_(arg2_1), arg2_2_(arg2_2) {}
    void use() override { std::cout << "Using Product2 with arg2_1: " << arg2_1_ << ", arg2_2: " << arg2_2_ << std::endl; }
private:
    int arg2_1_;
    std::string arg2_2_;
};

class ProductFactory {
public:
    static ProductFactory& getInstance() {
        static ProductFactory instance;
        return instance;
    }

    template <typename T, typename... Args>
    //typename std::enable_if<std::is_same<Product,T>::value, void>::type
    std::unique_ptr<T> createProduct(Args&&... args) {
        return std::make_unique<T>(std::forward<Args>(args)...);
    }

private:
    ProductFactory() = default; // Private constructor for singleton
};

int main() {
    ProductFactory factory;

    std::unique_ptr<Product> product1 = factory.createProduct<Product1>(42);
    std::string hi("Hello");
    std::unique_ptr<Product> product2 = factory.createProduct<Product2>(5, hi);

    product1->use();
    product2->use();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/950425.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【轻松学C:编程小白的大冒险】--- C语言简介 02

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【轻松学C&#xff1a;编程小白的大冒险】…

下载b站高清视频

需要使用的edge上的一个扩展插件&#xff0c;所以选择使用edge浏览器。 1、在edge浏览器上下载 强力视频下载合并 扩展插件 2、在edge上打开b站&#xff0c;登录自己账号&#xff08;登录后才能下载到高清&#xff01;&#xff01;&#xff09;。打开一个视频&#xff0c;选择自…

oxml中创建CT_Document类

概述 本文基于python-docx源码&#xff0c;详细记录CT_Document类创建的过程&#xff0c;以此来加深对Python中元类、以及CT_Document元素类的认识。 元类简介 元类&#xff08;MetaClass&#xff09;是Python中的高级特性。元类是什么呢&#xff1f;Python是面向对象编程…

Tableau数据可视化与仪表盘搭建-基础图表制作

目录 对比分析&#xff1a;比大小 柱状图 条形图 数据钻取 筛选器 热力图 气泡图 变化分析&#xff1a;看趋势 折线图 预测 面积图 关系分布&#xff1a;看位置 散点图 直方图 地图 构成分析&#xff1a;看占比 饼图 树地图 堆积图 对比分析&#xff1a;比大…

QML学习(八) Quick中的基础组件:Item,Rectangle,MouseArea说明及使用场景和使用方法

上一篇中我们从设计器里可以看到Qt Quick-Base中有几大基础组件&#xff0c;如下图&#xff0c;这篇文章先介绍下Item&#xff0c;Rectangle&#xff0c;MouseArea这三个的说明及使用场景和使用方法 Item Item 是 QML 中所有可视元素的基类&#xff0c;是一个非常基础和通用的…

万界星空科技质量管理QMS系统具体功能介绍

一、什么是QMS系统&#xff0c;有什么价值&#xff1f; 1、QMS 系统即质量管理系统&#xff08;Quality Management System&#xff09;。 它是一套用于管理和控制企业产品或服务质量的集成化体系。 2、QMS 系统的价值主要体现在以下几个方面&#xff1a; 确保产品质量一致性…

字符串哈希stl解决

题目如下 STL的unordered-set STL的map 谢谢观看&#xff01;&#xff01;&#xff01;

JAVA I/O流练习1

往D盘中的JAVA复习文件夹中写数据&#xff1a; 数据改了一下哈&#xff1a; import java.io.*; import java.util.Scanner; public class Test {public static void main(String[] args) throws IOException {String fileName"D:JAVA复习\\grade.txt";FileWriter w…

英伟达Project Digits赋能医疗大模型:创新应用与未来展望

英伟达Project Digits赋能医疗大模型&#xff1a;创新应用与未来展望 一、引言 1.1 研究背景与意义 在当今数字化时代&#xff0c;医疗行业作为关乎国计民生的关键领域&#xff0c;正面临着前所未有的挑战与机遇。一方面&#xff0c;传统医疗模式在应对海量医疗数据的处理、复…

OpenAI 故障复盘 - 阿里云容器服务与可观测产品如何保障大规模 K8s 集群稳定性

本文作者&#xff1a; 容器服务团队&#xff1a;刘佳旭、冯诗淳 可观测团队&#xff1a;竺夏栋、麻嘉豪、隋吉智 一、前言 Kubernetes(K8s)架构已经是当今 IT 架构的主流与事实标准&#xff08;CNCF Survey[1]&#xff09;。随着承接的业务规模越来越大&#xff0c;用户也在使…

移动电商的崛起与革新:以开源AI智能名片2+1链动模式S2B2C商城小程序为例的深度剖析

摘要&#xff1a;本文旨在探讨移动电商的崛起背景、特点及其对传统电商模式的革新影响&#xff0c;并以开源AI智能名片21链动模式S2B2C商城小程序为具体案例&#xff0c;深入分析其在移动电商领域的创新实践。随着移动互联网技术的飞速发展&#xff0c;移动电商已成为电商行业的…

el-table 合并单元格

参考文章&#xff1a;vue3.0 el-table 动态合并单元格 - flyComeOn - 博客园 <el-table :data"tableData" border empty-text"暂无数据" :header-cell-style"{ background: #f5f7fa }" class"parent-table" :span-method"obj…

C/C++进阶-函数

C/C入门-函数起始 函数引用与指针函数参数 指针写法 和 数组写法数组的引用右值引用概念&#xff1a;**反汇编&#xff1a;**总结用结构体的示例再理解一遍 函数的本质栈分析栈溢出攻击 函数重载函数重载 进阶 思考函数重载补充 函数模板&#xff08;1&#xff09;&#xff08;…

通俗易懂之线性回归时序预测PyTorch实践

线性回归&#xff08;Linear Regression&#xff09;是机器学习中最基本且广泛应用的算法之一。它不仅作为入门学习的经典案例&#xff0c;也是许多复杂模型的基础。本文将全面介绍线性回归的原理、应用&#xff0c;并通过一段PyTorch代码进行实践演示&#xff0c;帮助读者深入…

分布式主键ID生成方式-snowflake雪花算法

这里写自定义目录标题 一、业务场景二、技术选型1、UUID方案2、Leaf方案-美团&#xff08;基于数据库自增id&#xff09;3、Snowflake雪花算法方案 总结 一、业务场景 大量的业务数据需要保存到数据库中&#xff0c;原来的单库单表的方式扛不住大数据量、高并发&#xff0c;需…

在 C# 中显示动画 GIF 并在运行时更改它们

您可以通过将按钮、图片框、标签或其他控件的Image属性设置为 GIF 文件 来显示动画 GIF 。&#xff08;如果您在窗体的BackgroundImage属性中显示一个&#xff0c;则不会获得动画。&#xff09; 有几种方法可以在运行时更改 GIF。 首先&#xff0c;您可以将 GIF 添加为资源。…

【技术支持】安卓无线adb调试连接方式

Android 10 及更低版本&#xff0c;需要借助 USB 手机和电脑需连接在同一 WiFi 下&#xff1b;手机开启开发者选项和 USB 调试模式&#xff0c;并通过 USB 连接电脑&#xff08;即adb devices可以查看到手机&#xff09;&#xff1b;设置手机的监听adb tcpip 5555;拔掉 USB 线…

【网络】计算机网络的分类 局域网 (LAN) 广域网 (WAN) 城域网 (MAN)个域网(PAN)

局域网是通过路由器接入广域网的 分布范围 局域网Local Area Network&#xff1a;小范围覆盖&#xff0c;速度高&#xff0c;延迟低(办公室&#xff0c;家庭&#xff0c;校园&#xff0c;网络) 广域网Wide Area Network 大范围覆盖&#xff0c;速度相对低&#xff0c;延迟高…

scanf:数据之舟的摆渡人,静卧输入港湾的诗意守候

大家好啊&#xff0c;我是小象٩(๑ω๑)۶ 我的博客&#xff1a;Xiao Xiangζั͡ޓއއ 很高兴见到大家&#xff0c;希望能够和大家一起交流学习&#xff0c;共同进步。* 这一节我们主要来学习scanf的基本用法&#xff0c;了解scanf返回值&#xff0c;懂得scanf占位符和赋值…

win10 gt520+p106双卡测试

安装391.35驱动失败,虽然gpuz和设备管理器显示正常但没有nvidia控制面板 重启进安全模式,ddu卸载,再次重启到安全模式,安装391.01驱动,显示3dvision安装失败,重启再看已经有nvidia控制面板了 修改p106注册表 AdapterType 1 EnableMsHybrid 1 计算机\HKEY_LOCAL_MACHINE\SYSTE…